문제
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을 출력한다.
예제 입력 1
6 4
0100
1110
1000
0000
0111
0000
예제 출력 1
15
예제 입력 2
4 4
0111
1111
1111
1110
예제 출력 2
-1
*문제해석
이 문제는 '그래프 탐색' 문제다.
그 중에서 BFS를 활용하는 문제다. BFS문제를 풀기 위해 큐를 사용했다. 이 문제는 14502 연구실 문제처럼 백트래킹을 활용해 풀려하면 시간복잡도가 엄청나게 커지게 된다.
이를 해결하기 위해 3차원배열을 사용한다.
3차원 배열로 만들어 [][][0] 이면 벽을 한번도 부수지 않은거고 [][][1]이면 벽을 이미 부순 후라 더 이상 부술 수 없음을 의미한다.
0이면 벽을 뚫을 수 있는 경로를 탐색하고 1이면 그냥 단순한 경로 탐색을 실시하면된다.
결국 BFS를 하면서 벽을 뚫는 경우와 뚫지 않은 경우 2가지를 탐색 후 가장 최단 거리를 출력하는 것이다.
*나의 코드
import sys
from collections import deque
input = sys.stdin.readline
def bfs():
queue = deque()
queue.append([0, 0, 0])
while queue:
x, y, w = queue.popleft()
# 목적지에 도달하였다면 return
if x == n - 1 and y == m - 1:
return visited[x][y][w]
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if nx <= -1 or nx >= n or ny <= -1 or ny >= m:
continue
# 다음 이동할 곳이 벽이고 벽 부수기를 사용하지 않았다면
if graph[nx][ny] == 1 and w == 0:
visited[nx][ny][1] = visited[x][y][0] + 1
queue.append([nx, ny, 1])
# 다음 이동할 곳이 벽이 아니고 아직 방문을 한번도 하지 않았다면
if graph[nx][ny] == 0 and visited[nx][ny][w] == 0:
visited[nx][ny][w] = visited[x][y][w] + 1
queue.append([nx, ny, w])
return -1
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
n, m = map(int, input().split())
graph = []
for _ in range(n):
graph.append(list(map(int, input().strip())))
#3차원 배열 선언
visited = [[[0] * 2 for _ in range(m)] for _ in range(n)]
visited[0][0][0] = 1
print(bfs())
어려운 문제였다고 생각한다. 단순한 경로 탐색이아닌 벽을 뚫는 조건이 추가되어 그것을 포함하여 코드를 짜려고 하니 생각보다 쉽지않았다. 포인트는 '단 한번' 벽을 부술 수 있던 점이었다. 2차원 배열만 사용하게 될 경우 각 위치마다 벽을 부술 수 있는지를 확인해야 하기 때문에 시간 복잡도가 O(NM^2)인 1조가 된다. 1번만 부술 수 있다는 점이 벽이 2중으로 있는 경우를 생각해야해서 일일히 확인하다보니 시간이 엄청나게 늘어나게 된다.
그걸 방지하기 위해 3차원배열이 사용되었다.
*중요한 점
1. 방문여부에 '단 한번만 벽을 부술 수 있다' 같은 조건이 추가될 경우 visited배열을 3차원으로 사용한다.
'코딩테스트 > 백준' 카테고리의 다른 글
11. 백준 1715 (골드4) : 카드 정렬하기 _ python풀이 (0) | 2024.08.25 |
---|---|
10. 백준 7569 (골드5) : 토마토 _ python풀이 (0) | 2024.08.23 |
8. 백준 14502 (골드4) : 연구소 _ python풀이 (0) | 2024.08.17 |
7. 백준 7562 (실버1) : 나이트의 이동 _ python풀이 (0) | 2024.08.14 |
6. 백준 20920 (실버3) : 영단어 암기는 괴로워 _ python풀이 (0) | 2024.08.13 |