💻 백준 1238번: 파티
- 문제 링크: https://www.acmicpc.net/problem/1238
- 알고리즘 분류: 다익스트라(Dijkstra)
문제 소개 🧐
N명의 학생들이 각자 다른 마을에 살고 있다. 이들이 X번 마을에 모여 파티를 하기로 했다. 마을 사이에는 M개의 단방향 도로가 있으며, 각 도로를 지나는 데는 특정 시간이 걸린다.
모든 학생은 자신의 마을에서 X번 마을까지 갔다가 다시 자신의 마을로 돌아와야 한다. 이때, 최단 시간으로 오고 가고 싶어 한다.
N명의 학생 중, 이 왕복 과정에 가장 많은 시간이 걸리는 학생은 누구이며, 그 시간은 얼마인지 구하는 문제다.
입력 📝
- 첫째 줄에 N(학생 수/마을 수), M(도로 수), X(파티 장소)가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ M ≤ 10,000)
- 다음 M개의 줄에 걸쳐 도로의 시작점, 끝점, 소요 시간이 주어진다.
출력 📤
- N명의 학생 중 가장 오래 걸리는 학생의 총 소요 시간을 출력한다.
🤔 아이디어: 다익스트라의 필요성
모든 노드 쌍 간의 최단 경로를 구해야 하는 문제처럼 보일 수 있다. 만약 그랬다면 플로이드-워셜 알고리즘을 고려했을 것이다. 하지만 플로이드-워셜은 시간 복잡도가 O(N³)이므로, N이 1,000일 때 약 10억 번의 연산이 필요해 시간 초과가 날 것이 분명했다.
따라서, 특정 지점을 기준으로 하는 최단 경로 알고리즘인 다익스트라(Dijkstra) 를 사용하였다.
핵심은 두 가지 경로의 최단 시간을 구하는 것이다.
- 각 마을
i
에서 파티 장소X
까지 가는 최단 시간 - 파티 장소
X
에서 각 마을i
로 돌아오는 최단 시간
🔄첫 번째 접근: 각 학생마다 다익스트라 실행
가장 직관적인 방법으로 각 학생(마을)마다 다익스트라 알고리즘을 실행했다.
- 모든 학생
i
(1부터 N까지)에 대해,dijkstra(i)
를 실행하여X
까지의 최단 거리를 구한다. (i -> X
) - 파티 장소
X
에서dijkstra(X)
를 실행하여 모든 다른 마을i
까지의 최단 거리를 구한다. (X -> i
) - 각 학생
i
에 대해(i -> X) + (X -> i)
시간을 계산하고, 이 중 최댓값을 찾는다.
Code
import sys
import heapq
N, M, X = map(int, sys.stdin.readline().split())
graph = [[] for _ in range(N+1)]
for _ in range(M):
a, b, t = map(int, sys.stdin.readline().split())
graph[a].append((t, b))
def dijkstra(start, graph):
global N
q = []
q.append((0, start))
path = [float('inf')] * (N+1)
path[start] = 0
while q:
time, node = heapq.heappop(q)
if path[node] < time:
continue
for n_t, n_node in graph[node]:
n_time = time + n_t
if n_time < path[n_node]:
path[n_node] = n_time
heapq.heappush(q, (n_time, n_node))
return path
# X에서 집으로 돌아오는 최단 경로
x_path = dijkstra(X, graph)
answer = 0
for i in range(1, N+1):
if i == X:
continue
# 집에서 X로 가는 최단 경로
cur = dijkstra(i, graph)
answer = max(answer, cur[X] + x_path[i])
print(answer)
Result
- 결과: 성공! 🎉
마무리 🤔
- 위의 방법은
N
명의 학생에 대해 다익스트라를 각각 실행하므로, 총N
번의 다익스트라 연산이 필요하다. 다익스트라의 시간 복잡도는 O(M log N)이므로, 총 시간 복잡도는 O(N * M log N)이 된다. N=1,000, M=10,000일 경우 비효율적일 수 있다.
시간 복잡도를 줄일 더 효율적인 방법이 있다. 바로 역방향 그래프를 활용하는 것이다.
-
X -> i
(돌아오는 길): 원래 그래프에서 다익스트라를 한 번만 실행하면X
에서 모든 다른 마을i
까지의 최단 거리를 모두 구할 수 있다. -
i -> X
(가는 길): 모든 마을i
에서X
로 가는 최단 거리를 어떻게 한 번에 구할 수 있을까? 도로의 방향을 모두 뒤집은 역방향 그래프를 만들고,X
에서부터 다익스트라를 실행하면 된다. 즉, 역방향 그래프에서X
로부터i
까지의 최단 거리는 원래 그래프에서i
로부터X
까지의 최단 거리와 같다.
이 방법을 사용하면 다익스트라 알고리즘을 단 두 번만 실행하면 된다.
-
dist_from_X
: 원래 그래프에서dijkstra(X)
실행 -
dist_to_X
: 역방향 그래프에서dijkstra(X)
실행 - 각 학생
i
의 총 소요 시간 =dist_to_X[i] + dist_from_X[i]
이 접근법의 시간 복잡도는 O(M log N) 으로, 훨씬 효율적이다.
- 핵심: 다익스트라 알고리즘을 두 번 사용하여 문제를 해결한다.
-
가는 길: 역방향 그래프를 만들어
X
에서 출발하는 다익스트라를 통해 모든 노드에서X
까지의 최단 거리를 구한다. -
오는 길: 원래 그래프에서
X
에서 출발하는 다익스트라를 통해X
에서 모든 노드까지의 최단 거리를 구한다.
‘모든 정점에서 특정 한 정점까지의 최단 경로’는 그래프를 뒤집어 ‘특정 한 정점에서 모든 정점까지의 최단 경로’ 문제로 바꿀 수 있다는 교훈을 얻었다.
⁂