문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/67259
프로그래머스
SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
풀이
처음에 문제를 풀 때, 최소 거리가 최소 비용을 보장하지 않을 것으로 판단되어서 DFS로 풀이하였다. 하지만, 일부 테스트 케이스에서 시간 초과가 발생하여 다른 방법으로 풀이해야 했다. (풀이를 검색해 보니 DFS + 메모이제이션 방식으로 풀이할 수 있다고도 한다.)
###### DFS 방식으로 풀이, 시간 초과로 인해 실패 ######
def solution(board):
dirs = [(1, 0, False), (-1, 0, False), (0, 1, True), (0, -1, True)]
n = len(board)
visited = [[False for _ in range(n)] for _ in range(n)]
visited[0][0] = True
min_cost = float('inf')
def dfs(x, y, cost, is_hor):
nonlocal min_cost
if cost >= min_cost:
return
if x == n-1 and y == n-1:
min_cost = min(min_cost, cost)
return
for dir in dirs:
dx, dy, is_d_hor = dir
nx = x + dx
ny = y + dy
if nx < 0 or nx >= n or ny < 0 or ny >= n or board[nx][ny] == 1 or visited[nx][ny]:
continue
visited[nx][ny] = True
dfs(nx, ny, cost + (100 if is_hor is None or is_hor == is_d_hor else 600), is_d_hor)
visited[nx][ny] = False
dfs(0, 0, 0, None)
return min_cost
위와 같이 기존 DFS로 구현했던 코드에서 크게 변환하지 않을 수 있는 다른 풀이 방법을 찾아보았다. 우선 순위 큐를 사용한 BFS(다익스트라)를 사용하는 방식으로 코드를 변경했다.
이 문제를 BFS를 사용해서 풀이할 수 있는 이유는 각 좌표에서 다음 좌표로 이동할 때 가중치가 다르기에 (코너와 직선 구간에서의 비용이 다르기 때문) 마치 다익스트라 문제를 푸는 것처럼 BFS + 우선순위 큐를 사용해서 풀이할 수 있는 것이다. 그리고 당연하게도 우선순위 큐를 사용해서 최솟값인 부분을 먼저 체크하기에 DFS보다 더 빠른 시간에 풀이가 가능해진다.
import heapq
# 방향이 전환된 경우인지 확인할 필요가 있으므로, 이전의 이동 방향이 가로인지 세로 방향인지를 표시해둘 필요가 있음
HOR = 1
VER = 0
def solution(board):
n = len(board)
dirs = [(1, 0, VER), (-1, 0, VER), (0, 1, HOR), (0, -1, HOR)] # 다음으로 (x좌표를 얼마나 이동할지 , y좌표를 얼마나 이동할지, 이동 방향이 세로인지 가로인지) 튜플들 저장
cost = [[[float('inf')] * 2 for _ in range(n)] for _ in range(n)] # 가로 또는 세로로 이동할 때 각 좌표에서 나올 수 있는 비용의 최솟값을 저장할 배열
pq = []
heapq.heappush(pq, (0, 0, 0, None)) # 우선순위 큐에 초기값 저장 (초기 이동 방향은 None으로 설정)
while pq: # BFS 시작
cur_cost, x, y, is_hor = heapq.heappop(pq) # 현재 비용이 최소인 경우부터 확인
if x == n - 1 and y == n - 1: # 목적지에 도달했으면
return cur_cost # 그 때의 비용이 최소 비용이 됨
for (dx, dy, d_is_hor) in dirs: # 현재 위치에서 다음 위치로 이동할 수 있는 경우를 체크하기
nx, ny = x + dx, y + dy # 다음 위치의 x좌표, y좌표
if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 0: # x좌표와 y좌표가 board 내부에 위치하고, 그 좌표에 경주로 설치가 가능하다면
# 만약 최초의 이동인 경우(is_hor가 None)이거나 이전 이동 방향과 동일하다면 직선 경주로만 설치하면 되기에 100만 더해주고, 이전 이동 방향과 동일하지 않다면 곡선 경주로를 설치해야하기에 500을 추가한 600을 더해줌
new_cost = cur_cost + (100 if is_hor is None or is_hor == d_is_hor else 600) # 그 좌표에 경주로를 건설하는데 드는 비용 계산
if new_cost < cost[nx][ny][d_is_hor]: # 만약, 현재 이동에서의 비용이 이전까지의 이동에서의 비용보다 작다면 이는 계속해서 확인할 가치가 있는 이동 경로임
cost[nx][ny][d_is_hor] = new_cost # 현재 좌표, 현재 이동 방향에서의 최솟값 갱신
heapq.heappush(pq, (new_cost, nx, ny, d_is_hor)) # 우선순위 큐에 집어넣어 이후 경로를 체크하도록 함
'알고리즘 > 알고리즘 문제 풀이' 카테고리의 다른 글
[프로그래머스 Lv.2] 게임 맵 최단거리 (Python) (3) | 2025.01.20 |
---|---|
[프로그래머스 Lv.3] 단어 변환 (Python) (2) | 2025.01.13 |
[프로그래머스 Lv.2] 괄호 변환 (Python) (0) | 2024.12.30 |
[프로그래머스 Lv.3] 여행경로 (Python) (0) | 2024.12.16 |
[프로그래머스 Lv.2] 타겟 넘버 (Python) (1) | 2024.12.08 |