어딘가 낯이 익은 문제여서 제출 이력을 열었더니 2024년 11월 치열하게 풀었던 흔적이 보였다.
반년이 지난 지금, 그래도 조금은 성장했나보다.
문제 설명
n × m 크기의 던전이 있다.
이 던전은 격자 형태로 구성되어 있으며, 각 방은 상하좌우로 인접한 방으로 이동할 수 있다.
2차원 배열 moveTime이 주어진다.
moveTime[i][j]는 (i, j) 위치의 방으로 이동을 시작할 수 있는 최소 시간(초)을 의미한다.
초기 위치는 (0, 0)이며 시작 시간은 0초이다.
인접한 방으로의 이동은 항상 1초가 소요된다.
목표는 (n - 1, m - 1) 위치까지 이동하는 데 걸리는 최소 시간을 구하는 것이다.
예시
예제 1
입력: moveTime = [[0,4],[4,4]]
출력: 6
설명:
t = 0 → (0,0)
t = 4 → 이동 시작 → (1,0)
t = 5 → 이동 시작 → (1,1)
t = 6 도착
예제 2
입력: moveTime = [[0,0,0],[0,0,0]]
출력: 3
설명:
t = 0 → (0,0)
t = 1 → (1,0)
t = 2 → (1,1)
t = 3 → (1,2) 도착
예제 3
입력: moveTime = [[0,1],[1,2]]
출력: 3
설명:
t = 0 → (0,0)
t = 1 → (1,0)
t = 2 → (1,1)
t = 3 도착
조건(제약사항)
- 2 ≤ n ≤ 50
- 2 ≤ m ≤ 50
- 0 ≤ moveTime[i][j] ≤ 10⁹
- 각 이동은 오직 인접한 상하좌우 방으로만 가능하다.
- 한 방에서 여러 번 대기할 수 있다.
풀이전략
풀이 전략을 짜기 전 핵심은 특정 위치 값이 minimum_t 라면 해당 위치에 도달했을 때 시간은 minimum_t + 1 이라는 점이다.
인접한 현재 위치에서 출발을 시작해도 되는 최소 시간을 뜻하기 때문인데, 문제를 이해하는데 어려움을 주는 요소였다.
이걸 놓치면 계속 틀린 답이 나오니 주의하기.
- 문제 유형 파악
- 이 문제는 최단 경로 문제다.
- 각 노드(방)에 대해 도달 가능한 시간 제한이 존재한다.
- 일반적인 BFS보다는 우선순위 큐를 활용한 Dijkstra 알고리즘이 적절하다.
- 우선순위 큐 사용 (Dijkstra 방식)
- 각 상태를 (좌표, 시간) 구성하여, 도달 시간이 빠른 순서대로 처리한다.
- 다음 방으로 이동할 수 있는 최소 시간은 max(current_time + 1, moveTime[next_x][next_y] + 1)이다.
→ 현재 시간에 1초를 더한 값과, 해당 방이 허용하는 최소 시간 + 1 중 큰 값을 선택한다.
- 방문 처리
- 이미 더 빠른 시간에 방문한 적이 있다면 해당 경로는 스킵한다.
- 종료 조건
- (n-1, m-1)에 도달하면 즉시 해당 시간을 반환한다.
BFS 풀이 - 우선 빠르게 풀어본다
물론 BFS로도 풀 수 있다.
예상했던 Time Limit Exceed 가 뜨지는 않는다.
from collections import deque
class Solution:
def minTimeToReach(self, moveTime: List[List[int]]) -> int:
n, m = len(moveTime), len(moveTime[0])
chk = [[-1 for j in range(m)] for i in range(n)]
moveTime[0][0], chk[0][0] = 0, 0
q = deque()
dr = (0, 1, 0, -1)
dc = (1, 0, -1, 0)
q.append((0, 0, moveTime[0][0]))
while len(q) != 0:
r, c, t = q.popleft()
for i in range(4):
nr, nc = r + dr[i], c + dc[i]
if nr < 0 or n <= nr or nc < 0 or m <= nc:
continue
if chk[nr][nc] != -1 and chk[nr][nc] <= t + 1:
continue
chk[nr][nc] = max(t + 1, moveTime[nr][nc] + 1)
q.append((nr, nc, chk[nr][nc]))
return chk[n - 1][m - 1]
BFS 와 Dijkstra 알고리즘은 FIFO Queue 를 사용하느냐 Priority Queue 를 사용하느냐라고 봐도 무방하다.
혹시 이 풀이를 보기 위해 들어오신 분들은 우선순위 큐로 바꿔서 시도해보면 좋겠다.
BFS로 풀면 100명 중 95등이라는 처참한 숫자가 나온다.
Dijkstra 풀이
앞서 언급한 것처럼 Queue를 Priority Queue로 바꿔준다.
내가 Python으로 문제를 풀 때에는 heapq 모듈을 사용한다.
import heapq
class Solution:
def minTimeToReach(self, moveTime: List[List[int]]) -> int:
n, m = len(moveTime), len(moveTime[0])
chk = [[-1 for j in range(m)] for i in range(n)]
moveTime[0][0], chk[0][0] = 0, 0
dr = (0, 1, 0, -1)
dc = (1, 0, -1, 0)
heap = []
heapq.heappush(heap, (chk[0][0], (0, 0)))
while len(heap) != 0:
t, (r, c) = heapq.heappop(heap)
if r == n - 1 and c == m - 1:
return t
for i in range(4):
nr, nc = r + dr[i], c + dc[i]
if nr < 0 or n <= nr or nc < 0 or m <= nc:
continue
if chk[nr][nc] != -1 and chk[nr][nc] <= t + 1:
continue
chk[nr][nc] = max(t + 1, moveTime[nr][nc] + 1)
heapq.heappush(heap, (chk[nr][nc], (nr, nc)))
return chk[n - 1][m - 1]
이렇게 풀어주면 적어도 50% 정도에는 드는 코드가 나온다.
BFS로 빠르게 구현 견적을 봤다면 Runtime 성능 개선을 위헤 음의 가중치가 없는 것을 확인하고, Priority Queue 로 바꿔보도록 하자.
요약(핵심)
- 이동 제한 시간이 존재하는 격자 최단 경로 문제이다.
- Dijkstra 알고리즘을 사용해 도달 가능한 최소 시간을 우선순위 큐로 관리한다.
- 다음 방으로 이동 가능한 시간은 max(현재 시간 + 1, moveTime[x][y] + 1)로 계산한다.
- 도달 지점에 도착하면 즉시 결과를 반환한다.
'Problem Solving > Leetcode' 카테고리의 다른 글
[Leetcode] 2918. Minimum Equal Sum of Two Arrays After Replacing Zeros (0) | 2025.05.11 |
---|---|
[Leetcode] 3342. Find Minimum Time to Reach Last Room II (1) | 2025.05.09 |
[Leetcode] 1920. Build Array from Permutation (0) | 2025.05.07 |
[Leetcode] 790. Domino and Tromino Tiling (0) | 2025.05.06 |
[Leetcode] 1007. Minimum Domino Rotations For Equal Row (0) | 2025.05.03 |