Processing math: 100%
본문 바로가기

알고리즘/백준 문제 풀이

[Python] 2206번 벽 부수고 이동하기

728x90

https://www.acmicpc.net/problem/2206

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net


 

24/03/19

 

 

BFS 응용 문제로, 클래스에 있는 아주 교육적인 문제다. 한번 더 생각해야 되는 좋은 문제다.


 

문제 접근 방식:

 

 

기본적으로 최단 거리를 구해야 되는 문제이기 때문에 BFS를 사용해야겠다는 감은 올 것이다.

 

그러면 어떻게 BFS를 구현할 것이냐가 문제인데, 벽을 최대 한 칸까지 뚫을 수 있다는 점이 핵심이다.

 

따라서 벽을 뚫는 행위를 어떻게 구현할 것인가가 이 문제의 핵심이 될 것이다.

 

해결법은 벽을 뚫는 행위를 했는가 하지 않았는가의 두 가지 경우로 나누어서, 방문 배열을 따로 관리하는 것이다.

 

(0,0)칸은 항상 벽이 없는 칸이라고 문제에서 제시되어 있으므로, 벽을 뚫는 행위를 하지 않은 경우의 방문 배열의 (0,0)칸을 방문 처리하고 큐에 담으면 된다.

 

이후 BFS를 진행하는데, 케이스를 다음과 같이 분류할 수 있다.

 

1. 지금까지 벽을 뚫는 행위를 하지 않았고, 앞으로 방문할 칸이 빈 칸일 경우

2. 지금까지 벽을 뚫는 행위를 하지 않았고, 앞으로 방문할 칸이 벽일 경우

3. 이전에 벽을 뚫는 행위를 했었고, 앞으로 방문할 칸이 빈 칸일 경우

4. 이전에 벽을 뚫는 행위를 했었고, 앞으로 방문할 칸이 벽일 경우

 

1번 경우는 벽을 뚫는 행위를 하지 않고 그냥 다음 칸을 방문처리 할 수 있다. 벽을 뚫는 행위를 하지 않았을 때의 방문 배열에 방문 처리를 해주면 충분하다.

 

2번 경우는 벽을 뚫는 행위를 해야만 한다. 따라서 벽을 뚫었을 때의 방문 배열에 방문 처리를 해주면 된다.

 

3번 경우는 이미 내가 벽을 뚫는 행위를 했었으므로, 벽을 뚫었을 때의 방문 배열에 방문 처리를 해주면 된다.

 

4번 경우는 그 칸은 방문할 수 없다. 따라서 그냥 처리하지 않아도 충분하다.

 

4가지 경우를 고려하여 방문 배열에 적절히 방문처리를 해주며 BFS를 진행하면 된다.

 

나의 경우, 방문 배열을 3차원 배열로 설정하여 방문 배열의 인덱스에 몇 번 벽 뚫는 행위를 했는지를 알아보기 쉽도록 구현했다.

 


아래는 내가 위의 접근 방식과 같이 작성한 파이썬 코드이다. 더보기를 누르면 확인할 수 있다.

더보기
# 2206번 벽 부수고 이동하기
# BFS
'''
접근 방법:
벽을 뚫었는지 뚫지 않았는지의 여부를 나타내는 변수를 하나 추가한다.
'''
import sys
input = sys.stdin.readline
from collections import deque

dr, dc = [-1, 1, 0, 0], [0, 0, -1, 1]
N, M = map(int, input().split())
graph = [list(map(int, input().rstrip())) for _ in range(N)]
visited = [[[0 for _ in range(M)] for _ in range(N)] for _ in range(2)]
queue = deque()
queue.append((0, 0, 0, 1))  # 벽 뚫기 사용 여부, 위치, 거리
visited[0][0][0] = 1
while queue:
    can_break, r, c, dist = queue.popleft()
    if r == N-1 and c == M-1:
        print(dist)
        break
    if can_break == 0:  # 벽 뚫기 사용 아직 안했으면
        for i in range(4):
            nr, nc = r+dr[i], c+dc[i]
            if 0 <= nr < N and 0 <= nc < M:
                if visited[0][nr][nc] == 0 and graph[nr][nc] == 0:
                    visited[0][nr][nc] = 1
                    queue.append((0, nr, nc, dist+1))
                if visited[1][nr][nc] == 0 and graph[nr][nc] == 1:
                    visited[1][nr][nc] = 1
                    queue.append((1, nr, nc, dist+1))
    else:  # 벽 뚫기를 이미 사용했다면
        for i in range(4):
            nr, nc = r+dr[i], c+dc[i]
            if 0 <= nr < N and 0 <= nc < M:
                if visited[1][nr][nc] == 0 and graph[nr][nc] == 0:
                    visited[1][nr][nc] = 1
                    queue.append((1, nr, nc, dist+1))
else:
    print(-1)