solved_ac[Class4][벽 부수고 이동하기](https://www.acmicpc.net/problem/2206)
N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.
만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.
한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.
맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.
첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.
첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.
6 4
0100
1110
1000
0000
0111
0000
15
4 4
0111
1111
1111
1110
-1
[백준]1260번: DFS와 BFS의 업그레이드 문제이다. 앞서 풀었던 미로찾기와 비슷한데 여기서 추가된 것은 벽을 한번은 뚫을 수 있다는 것이다. 무조건 한번을 뚫어야 되는 것이 아닌 필요에 의해서 뚫을수도 있고 안뚫을수도 있다는 얘기이다. 이게 많이 복잡하다. 여기서 key 포인트는 벽을 안뚫고 갈때와 벽을 뚫고 갈때를 구분해서 작성을 해야 된다는 것이다. 그리고 또한 미로찾기에서는 graph를 직접 업데이트 해가면서 마지막 배열의 값이 총 걸리는 거리를 의미했는데 여기서는 graph를 직접 업데이트 시켜주면 안된다. 방문한 곳의 숫자가 증가해 있어서 이걸 벽으로 보고 못 지나갈 수 있기 때문에 다른 리스트를 이용하여 업데이트 시켜줘야한다. 그리고 또한 정답을 출력할 때 visited 리스트를 루프를 돌아 0이 아닌 리스트를 찾아 맨 마지막 리스트의 값을 출력할 필요가 없다. 어차피 최단 경로이기 때문에 bfs 루프를 돌다가 queue에서 가장 먼저 popleft()가 되어 나오는 좌표가 맨 오른쪽 밑의 좌표와 같다면 그것이 가장 최단 경로의 좌표인 것이다. 하지만 bfs 루프를 전부 돌았는데도 아무것도 return이 되는 것이 없다면 끝의 좌표에 도달하지 못한것이기 때문에 -1을 return 해주면서 정답을 도출해내면 된다.
선언 방식
distance = [[[0 for k in range(4)] for j in range(3)] for i in range(2)]
print(distance)
[
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
]
혹은
distance=[[[0]*4]*3]*2
print(distance)
[
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
]
맨 마지막에 곱해준 배열이 층이라고 생각하면 편하다.
하지만 문제에서는 맨 마지막에 2개를 벽을 뚫은 경우와 벽을 뚫지 않은 경우로 나누지 않고 맨 앞에 배열 2개로 하였다. 그 이유는 바로 쌍으로 구분하기 위해서이다.
import sys
from collections import deque
N, M = map(int, sys.stdin.readline().split())
graph = [[0 for _ in range(M)] for _ in range(N)]
queue = deque()
queue.append([0, 0, 0])
for i in range(N):
graph[i] = list(map(int, sys.stdin.readline().rstrip()))
visited = [[[0] * 2 for _ in range(M)] for _ in range(N)]
visited[0][0][0] = 1
visited[0][0][1] = 1
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
def bfs():
while queue:
x, y, broken = queue.popleft()
if x == N - 1 and y == M - 1:
return visited[x][y][broken]
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if nx >= 0 and nx < N and ny >= 0 and ny < M:
if graph[nx][ny] == 1 and broken == 0:
visited[nx][ny][1] = visited[x][y][0] + 1
queue.append([nx, ny, 1])
elif graph[nx][ny] == 0 and visited[nx][ny][broken] == 0:
visited[nx][ny][broken] = visited[x][y][broken] + 1
queue.append([nx, ny, broken])
return -1
non_broken = False
broken = False
print(bfs())
3차원 배열에 대해서 전혀 알지 못하는 상태여서 초반에 많이 고전했다. 처음에 풀고 싶었던 방식은 broken이라는 flag 변수를 세워서 queue값에 위의 코드처럼 같이 넣어주고 만약 flag가 벽을 부순 flag이면 앞으로 벽을 못부수게 0만 찾아서 찾아가는식으로 코드를 짯다. 그런데 문제점이 위에서는 graph를 직접 업데이트 안하고 visited라는 리스트를 따로 만들어서 업데이트를 해줬는데 나는 graph를 직접 업데이트 해가면서 검사를 했기 때문에 갈 수 있는 경로인데도 숫자가 있으면 벽이라고 생각해서 못가는 반례가 나왔다.