- 문제 : https://www.acmicpc.net/problem/1525
문제 이해
- 3 x 3 행렬에서 0인 지점을 기준으로 bfs를 하는 문제다.
- 0이 상하좌우 움직이면서 i행 j열의 값이 (3 * i + j) 값이 아닌 경우 0과 swap한다.
- 0이 오른쪽 아래까지 갈 수 있는 최단 경로를 출력하는 문제다.
- 처음에는 (행, 열) : 숫자 형식으로 딕셔너리를 만들었는데, 이렇게 하니 17%에서 계속 틀렸다. 이유로는 0이 상하좌우로 움직일 때마다 전체 행렬의 모습이 조금씩 달라지는데 그걸 공통 visit로 처리를 해서 각 케이스를 모두 담아내지 못해서 그런 것 같다.
- 그래서 풀이를 찾아보니 전체 퍼즐 모양을 문자열로('123456780') 처리한 뒤, 그 문자열이 되기까지의 최단 경로를 딕셔너리에 저장하면 되었다. {퍼즐 모양(문자열) : 연산횟수} 형식이다.
- visit만 약간 형식이 다르고, 나머지는 일반 bfs로 풀면 된다.
풀이 로직
- 초기값을 입력받고 puzzle에 문자열로 저장한다.
- visit는 {'퍼즐순서' : 연산횟수}를 원소로 가지는 딕셔너리다.
- queue와 while문을 가지고 bfs를 진행하고, answer에 정답을 저장한다.
- 도착한 puzzle의 모양이 정답인 '123456780'인 경우 answer를 입력한다.
- 아직 정답에 도착하지 않았다면 문자열에서 0의 행, 열의 위치(x, y)를 찾은 다음, 이동 횟수(count)를 +1 한다.
- 상하좌우로 이동한 nx, ny의 값은 행렬의 값이므로, 이를 문자열에서의 인덱스 값으로 바꿔준다. nz = 3 * nx + y
- puzzle을 리스트로 바꾼 뒤, 문자열에서 nz의 값(0에서 상하좌우로 이동한 값)과 z의 값(0)을 바꾼다.
- 새로 바꾼 new_puzzle을 이전에 방문한 적이 없다면 딕셔너리 visit를 count로 갱신한 뒤 queue로 이동하면 된다.
정답 풀이
from collections import deque
puzzle = ""
for i in range(3):
puzzle += ''.join(list(input().split()))
visit = {puzzle : 0}
queue = deque()
queue.append(puzzle)
answer = -1
dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1]
while queue:
puzzle = queue.popleft()
count = visit[puzzle]
z = puzzle.index('0')
if puzzle == '123456780':
answer = count
x, y = z // 3, z % 3
count += 1
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < 3 and 0 <= ny < 3:
nz = nx * 3 + ny
puzzle_list = list(puzzle)
puzzle_list[z], puzzle_list[nz] = puzzle_list[nz], puzzle_list[z]
new_puzzle = ''.join(puzzle_list)
if new_puzzle not in visit.keys():
visit[new_puzzle] = count
queue.append(new_puzzle)
print(answer)
참고한 블로그 : [백준] 1525번 : 퍼즐
'백준 > Search' 카테고리의 다른 글
[bfs/백준] 14867번 : 물통 (0) | 2023.05.31 |
---|---|
[bfs/백준] 1039번 : 교환 (0) | 2023.05.26 |
[bfs/백준] 9019번 : DSLR (0) | 2023.05.26 |
[bfs/백준] 16397번 : 탈출 (0) | 2023.05.25 |
[bfs/백준] 3055번 : 탈출 (0) | 2023.05.25 |