2917번: 늑대 사냥꾼
첫째 줄에 N과 M (1 ≤ N, M ≤ 500)이 주어진다. 둘째 줄부터 N개 줄에는 숲의 지도가 주어진다. 지도에 'V'와 'J'는 딱 하나만 있고, 적어도 하나의 '+'가 있다.
www.acmicpc.net
필자는 다익스트라 알고리즘을 활용하여 풀었으나 사실 BFS와 방문체크를 통해도 해결할 수는 있다. 오히려 속도도 더 빠를 것이다.
일단 필자의 풀이를 보면, BFS를 활용하는 방법도 바로 자연스레 이해될 것이다.
사실 한 정점에서 다른 정점으로 갈때의 비용이 항상 1이기에 BFS를 생각하는 것이 더 자연스럽다.

위와 같이 지도 상에 나무( + )가 배치되어있다고 하자. 각 나무에 cost=0을 두고 BFS나 다익스트라 등을 통해 맵 전체 탐색을 하는 것이다.

그러면 위의 그림과 같이 숫자가 퍼져나간다.
이 부분 구현을 위해 맵 전체에 cost값을 매우 큰 값으로 설정해두고 다익스트라를 통해 갱신해가도 되지만,
방문체크를 하여 방문하지 않은 점에 cost를 기록해가는 BFS방식으로 해도 된다. BFS로 하는 것이 로그(우선순위 큐) 계산이 붙지 않아 더 빠르다. 그림에서 빨강색으로 X 표시된 부분은 비용 업데이트가 되지 않는다는 뜻이다. 다익스트라에서는 새로 들어오는 값이 기존의 값보다 크기 때문에 갱신이 안될 것이고, BFS에서는 이미 방문한 점을 다시 방문하려 하는 것이기에 갱신이 안된다.
위의 맵 탐색을 이어가면 아래와 같이 모든 비용이 매겨진다.

그러면 이제 V에서 시작하여 J까지 이동하면서 (빨강색 경로는 예시일 뿐이다. 다른 여러 가지 경로가 있을 수 있다) 비용이 가장 작은 값을 선택하여 답으로 리턴해주면 된다.
이 문제에 대한 풀이를 생각함에 있어, V에서 J로 가는 경로상에 거치는 점에서 모든 나무까지 거리의 파악하는 방법은 뭔가 직관적으로 좋지 않다는 것을 알 수 있다. 그래서 반대로 모든 나무를 전부 큐나 우선순위 큐등에 담아두고, 맵 전체에 최소 비용 값을 미리 다 구해두는 방법을 생각해내는 것이 핵심이라고 할 수 있다. 그리고 그때 항상 최소값이 기록된다. 만약에 추가적으로 가장 가까운 나무가 어떤 것이냐는 질문에 대한 답을 내고자 한다면, 지도 상에 비용을 구하는 과정에서 해당 비용이 어느 나무에서 온 것인지를 기록해두면 될 것이다. 그때는 큐에 pair 등을 활용하여 [비용, 나무번호]등을 기록하면 된다.

조금 더 추상화하면 위의 그림과 같이 이해할 수 있다. 각 나무에서 동심원을 그리며 파동이 전파되고 가장 먼저 도달한 점에 모두 방문체크를 하는 것이다. 어차피 가장 먼저 도착한 값이 최소비용이기 때문이다. 그렇게 모든 맵을 채웠을 때 종료하고, 이제 V에서 J로 이동만 하면 되는 것이다. 그 과정에서 최소비용 중의 최소비용을 찾으면 된다.
<주요 내용>
BFS, 다익스트라, 우선순위 큐, 방문체크
<코드>
import sys import heapq si = sys.stdin.readline def main(): n, m = [int(e) for e in si().split()] dr, dc = [0, 1, 0, -1], [-1, 0, 1, 0] big = 500+500+1 cost = [[big for _ in range(m)]for _ in range(n)] treepq, board = [], [] for i in range(n): board.append(si()[:-1]) for j in range(m): if board[i][j] == 'V': vr, vc = i, j elif board[i][j] == '+': cost[i][j] = 0 heapq.heappush(treepq, (0, (i, j))) while treepq: curr = heapq.heappop(treepq) currcost, cr, cc = curr[0], curr[1][0], curr[1][1] for i in range(4): nr, nc = cr+dr[i], cc+dc[i] if 0 <= nr < n and 0 <= nc < m: if cost[nr][nc] > 1+currcost: cost[nr][nc] = 1+currcost heapq.heappush(treepq, (cost[nr][nc], (nr, nc))) pq, mn = [], cost[vr][vc] heapq.heappush(pq, (-cost[vr][vc], (vr, vc))) visited = [[False for _ in range(m)]for _ in range(n)] while pq: curr = heapq.heappop(pq) currcost, cr, cc = -curr[0], curr[1][0], curr[1][1] if visited[cr][cc]: continue visited[cr][cc] = True mn = min(mn, currcost) if board[cr][cc] == 'J': break for i in range(4): nr, nc = cr+dr[i], cc+dc[i] # if 0 <= nr < n and 0 <= nc < m: if 0 <= nr < n and 0 <= nc < m and not visited[nr][nc]: heapq.heappush(pq, (-cost[nr][nc], (nr, nc))) print(mn) if __name__ == '__main__': main()