알고리즘/BFS

[BFS] 백준 2206번 : 벽 부수고 이동하기 - 파이썬 Python

코딩균 2021. 11. 21. 20:23
 

 

 

생각하기

미로 찾기와 비슷한 게임이라고 판단하여 BFS를 사용하여 마지막 정점까지 가는 최소를 구하기로 생각했다

미로 찾기 문제와 달리 까다로운 해결해야할 조건 하나는

"벽을 한개 깰 수 있다는 점"

 

각 정점에서 다른 정점으로 이동하는 경우는 다음과 같다

  • 빈칸 -> 빈칸 : 벽 깨기 유무와 상관 없이 모두 이동 가능
  • 빈칸 -> 벽 : 벽 깨지 않은 경우 만 이동 가능 / 벽을 깬 경우 이동 불가
  • 벽 -> 벽 : 이동 불가
  • 벽 -> 빈칸 : 벽 깨기 유무와 상관 없이 모두 이동 가능

 

백준 강의에서 정점이란

정점은 앞에 어떤 상황이건 간선에 대하여 항상 같은 일을 해야 함

이 정의가 지켜져야 정점이라는 것이 성립한다

 

따라서 큐에 넣어주는 정점의 형태를 조금 다르게 정의해야 한다

 

정점 ( row, col, 이동 횟수, 벽 파괴 유무)

 

이렇게 정점을 정의하여 앞에 어떤 상황이건 벽 파괴 유무 를 통해 파악하여 대처할 수 있다

 

 

구현하기

BFS를 하려면 방문하지 않은 정점에 대해서만 방문을 해야 하는데

이번에는 경우가 두가지가 있다

  • 벽을 아직 깨지 않았을 때 파생되는 경우
  • 벽을 깬 후에 파생되는 경우

이에 대해서는 3차원 리스트를 만들어서 진행한다

최소 이동횟수를 구하는 과정이기 때문에 3차원 리스트는 1억으로 초기화한다

 

 

from collections import deque
import sys
input = sys.stdin.readline
max_num = int(1e9)

N, M = map(int, input().split())

visited = [[[max_num for col in range(M)] for row in range(N)] for depth in range(2)]
# visited[row][col][0] : 벽 1회 깨기 전의 방문 최소 cnt 횟수
# visited[row][col][1] : 벽 1회 꺠기 후의 방문 최소 cnt 횟수


data = []

for _ in range(N):
    data.append(list(map(int, input().strip())))




dy = [0, 1, 0, -1]
dx = [1, 0, -1, 0]


# (0, 0)부터 시작해서 (N-1, M-1) 까지 가는 것으로
q = deque()


def bfs(col, row, cnt):

    q.append((col, row, cnt, False))
    visited[0][col][row] = cnt

    while q:

        cur = q.popleft()
        
        for d in range(4):
            ny = cur[0] + dy[d]
            nx = cur[1] + dx[d]

            if ny<0 or ny>=N or nx<0 or nx>=M:
                continue

            if ny == N-1 and nx == M-1:
                if cur[3] == False:
                    visited[0][ny][nx] = min(visited[0][ny][nx], cur[2]+1)
                else:
                    visited[1][ny][nx]= min(visited[1][ny][nx], cur[2]+1)

                continue

            
            # 1번 벽 깨기를 하지 않은 경우
            if cur[3] == False:
                # 1번도 벽깨기 안한 경우, 벽을 만나면 방문하지 않은 벽일 경우 깨기
                if data[ny][nx] == 1 and visited[1][ny][nx] == int(1e9):
                    visited[1][ny][nx] = cur[2]+1
                    q.append((ny, nx, cur[2]+1, True))
                
                # 1번도 벽깨기 안한 경우, 빈칸을 만나면 방문하지 않은 경우 진입
                if data[ny][nx] == 0 and visited[0][ny][nx] == int(1e9):
                    visited[0][ny][nx] = cur[2]+1
                    q.append((ny, nx, cur[2]+1, False))

            # 1번 이상 벽깨기를 한 후인 경우
            else:
                # 1번 이상 벽꺠기를 한 경우, 빈칸을 만나면 진행
                if data[ny][nx] == 0 and visited[1][ny][nx] == int(1e9):
                    visited[1][ny][nx] = cur[2]+1
                    q.append((ny, nx, cur[2]+1, True))

                # 1번이상 벽깨기 한 경우, 빈칸 아닌거 만나면 진행 X


    return min(visited[0][N-1][M-1], visited[1][N-1][M-1])

result = bfs(0, 0, 1)
if result == int(1e9):
    print(-1)
else:
    print(result)