일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 파이썬
- Django
- scc
- 개발자
- python
- alembic
- sqlalchemy
- 수학
- MYSQL
- SQL
- 백준
- C언어
- 가우스 소거법
- 아파치
- flask
- api서버
- 백엔드
- 리트코드
- BFS
- 강한 연결 요소
- 신입
- 구성적
- 취업
- 데이터베이스
- 웹서버
- 알고리즘
- FastAPI
- 위상 정렬
- 이분 탐색
- 테일러 급수
- Today
- Total
Devlog
[백준 1525] 퍼즐 본문
문제
위와 같은 3x3 퍼즐이 있을 때 퍼즐위의 숫자들을 오름차순으로 정렬할 수 있는 최소 이동 횟수를 구하는 문제입니다. 얼핏보면 그냥 평범한 백트래킹 혹은 BFS 문제처럼 보이지만 메모리 제한(25MB)이 걸려있습니다.
접근
일반적인 BFS를 이용한 최소 비용을 구하는 문제들은 visited라는 방분 여부 변수를 사용하는데 이 문제에서는 visitied 배열을 9차원 배열로 선언해야 합니다. (ex: visited[0][1][2][3][4][5][6][7][8]) 그렇게 되면 visited의 크기는 9^9가 되는데 이 길이는 억단위를 넘어가기 때문에 메모리 제한에 걸리게 됩니다. 그리고 이 문제에서의 퍼즐은 1~8,0 숫자가 반드시 중복되지 않고 한 번 씩만 나오기 때문에 상당히 비효율적인 구조 입니다. 즉, 이 문제는 방문 여부를 저장하는 방법을 찾아야 합니다.
풀이 (파이썬 기준)
3x3배열을 문자열로 바꾸고 이 문자열을 key로 하는 딕셔너리(혹은 해시맵)을 선언하면 됩니다.
1 2 3
4 5 6
7 8 0
을
123456780
딕셔너리를 선언하고 문자열을 key로 저장하게 되면, 방문한 퍼즐 구조만 visited에 저장하기 때문에 메모리를 절약할 수 있습니다.
결국 3x3형태의 2차원 배열을 문자열로 바꾼 다음 BFS를 진행하면 풀리는 문제 입니다.
코드 보기
import sys
import collections
import heapq
input = sys.stdin.readline
visited = collections.defaultdict(int)
def swap_chars(s, i, j):
# i위치와 j위치의 문자 swap하기
x, y = s[i], s[j]
return s[:i] + y + s[i+1:j] + x + s[j+1:]
a = ''
for _ in range(3):
_raw = ''.join(input()[:-1].split())
a += _raw
q = [(0, a)]
while q:
cnt, s = heapq.heappop(q)
if s == '123456780':
break
else:
p = 0
for p in range(0, 10):
if s[p] == '0':
break
# 위
if p >= 3:
next_s = swap_chars(s, p-3, p)
if visited[next_s] == 0 or visited[next_s] > cnt + 1:
visited[next_s] = cnt + 1
heapq.heappush(q, (cnt + 1, next_s))
# 아래
if p < 6:
next_s = swap_chars(s, p, p+3)
if visited[next_s] == 0 or visited[next_s] > cnt + 1:
visited[next_s] = cnt + 1
heapq.heappush(q, (cnt + 1, next_s))
# 왼쪽
if p % 3 > 0:
next_s = swap_chars(s, p-1, p)
if visited[next_s] == 0 or visited[next_s] > cnt + 1:
visited[next_s] = cnt + 1
heapq.heappush(q, (cnt + 1, next_s))
# 오른쪽
if p % 3 < 2:
next_s = swap_chars(s, p, p+1)
if visited[next_s] == 0 or visited[next_s] > cnt + 1:
visited[next_s] = cnt + 1
heapq.heappush(q, (cnt + 1, next_s))
if visited['123456780'] > 0 or a == '123456780':
print(visited['123456780'])
else:
print(-1)
일단 이 포스트에 올린 코드는 1128ms에 작동되는 코드가 맞고 아래는 코드 길이를 줄이기 위해 큐에 들어갈 요소를 문자열이 아닌 3x3으로 바꿔서 구현한 코드 입니다. 얕은 복사(copy())의 비용이 큰 나머지 시간이 오래 걸린 것 같군요.
'Problem Solving > 코딩문제풀기' 카테고리의 다른 글
[백준 1103] 게임 (0) | 2022.07.12 |
---|---|
[백준 1039] 교환(Python) (0) | 2022.06.27 |
[백준 2133, 13976] 타일 채우기 1, 타일 채우기 2 (0) | 2022.06.19 |
[백준 1135] - 뉴스 전하기 (0) | 2022.06.16 |
[백준 2631번] - 줄세우기 (0) | 2022.06.11 |