BOJ 1238 파티
BOJ 1238 파티
문제
파티 https://www.acmicpc.net/problem/1238
$N$개의 마을에 사는 학생들이 $X$번 마을에 모여 파티를 연 후 다시 자기 마을로 돌아올 때, 각 학생의 ‘최단 왕복 시간’ 중 가장 오래 걸리는 학생의 소요 시간을 구하는 문제입니다.
- $N \le 1,000$ / $M \le 10,000$
- 도로 가중치 $T \le 100$ (단방향)
- 시간 제한: 1초 / 메모리 제한: 128MB
복잡도 분석
- Time Complexity: $O(M \log N)$
- 우선순위 큐를 이용한 다익스트라 알고리즘을 2회 수행합니다.
- 각각 $O(M \log N)$이며, $10,000 \times \log(1,000) \approx 10^5$ 정도로 1초 제한을 매우 여유롭게 통과합니다.
- Space Complexity: $O(N + M)$
- 정방향/역방향 인접 리스트($2 \times M$)와 거리 배열($2 \times N$)을 사용하여 약 수 MB 정도의 메모리만 사용합니다.
접근법
핵심은 ‘모든 정점에서 하나의 목적지(X)로 모이는 최단 거리’를 어떻게 한 번에 구하느냐에 있습니다.
- 오는 길 (X → i): 정방향 그래프에서 $X$를 시작점으로 다익스트라를 돌립니다.
- 가는 길 (i → X): 모든 $i$에 대해 다익스트라를 $N$번 돌리는 것은 비효율적입니다. 간선 방향을 모두 뒤집은 역방향 그래프를 생성한 뒤, $X$에서 다익스트라를 1회 돌리면 원래 그래프에서 $i \to X$로 가는 최단 거리를 모두 구할 수 있습니다.
graph TD
subgraph "정방향 그래프 (X에서 각 마을로)"
X((X)) -- "Original Edges" --> i((i))
end
subgraph "역방향 그래프 (각 마을에서 X로)"
ri((i)) -- "Reversed Edges" --> rX((X))
end
X -- "Dijkstra 1" --> dist_back["dist_back[i] (X → i)"]
rX -- "Dijkstra 2" --> dist_go["dist_go[i] (i → X)"]
dist_back & dist_go --> Final["Max(dist_go[i] + dist_back[i])"]
풀이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int v, cost;
// 우선순위 큐에서 최소 힙으로 동작하게 하기 위해 연산자 오버로딩
bool operator<(const Edge& other) const {
return cost > other.cost;
}
};
int N, M, X;
vector<vector<Edge>> g1; // 정방향: X -> i (오는 길)
vector<vector<Edge>> g2; // 역방향: i -> X (가는 길)
vector<int> dist1;
vector<int> dist2;
void dijkstra(int start, vector<vector<Edge>> &g, vector<int> &dist) {
priority_queue<Edge> pq;
// [CRITICAL] 시작점의 거리는 반드시 0으로 초기화해야 합니다.
dist[start] = 0;
pq.push({start, 0});
while (!pq.empty()) {
Edge curr = pq.top();
pq.pop();
if (dist[curr.v] < curr.cost) continue;
for (const auto &next : g[curr.v]) {
if (dist[next.v] > curr.cost + next.cost) {
dist[next.v] = curr.cost + next.cost;
pq.push({next.v, dist[next.v]});
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
if (!(cin >> N >> M >> X)) return 0;
g1.resize(N + 1);
g2.resize(N + 1);
dist1.assign(N + 1, 1e9);
dist2.assign(N + 1, 1e9);
for (int i = 0; i < M; i++) {
int u, v, w;
cin >> u >> v >> w;
g1[u].push_back({v, w});
g2[v].push_back({u, w});
}
dijkstra(X, g1, dist1); // X에서 각 마을로 가는 최단 거리 (Back)
dijkstra(X, g2, dist2); // 각 마을에서 X로 오는 최단 거리 (Go)
int max_time = 0;
for (int i = 1; i <= N; i++) {
// 경로가 없는 경우는 문제 조건상 배제되나, 안전하게 체크
if (dist1[i] != 1e9 && dist2[i] != 1e9) {
max_time = max(max_time, dist1[i] + dist2[i]);
}
}
cout << max_time;
return 0;
}
Code Review
- 기점 초기화 누락 주의: 다익스트라 함수 내
dist[start] = 0;처리가 누락되면 모든 결과가INF로 나오거나 엉뚱한 값이 계산됩니다. 시작점 설정은 다익스트라의 제1법칙입니다. - 역방향 그래프 활용: ‘Many-to-One’ 최단 거리를 ‘One-to-Many’ 문제로 치환하는 역방향 그래프 테크닉은 $O(N \cdot M \log N)$을 $O(M \log N)$으로 줄여주는 핵심 아이디어입니다.
- 메모리 관리:
vector::assign을 사용하여resize와 초기화를 동시에 처리하면 코드가 더 견고해집니다.
This post is licensed under CC BY 4.0 by the author.