최소 신장 트리 (MST, Minimum Spanning Tree)
정의
최소 신장 트리 (Minimum Spanning Tree, MST) 는 연결 그래프 G = (V, E) 에서 모든 정점을 연결 하면서 간선 가중치 합이 최소 인 부분 그래프. 정확히 V - 1 개의 간선으로 이루어진 트리.
두 대표 알고리즘: Kruskal (간선 정렬 + Union-Find), Prim (정점 기준 greedy). 둘 다 O((E + V) log V) 또는 O(E log E).
문제 상황과 동기
N 개 도시를 연결하는 최소 비용 도로망, 네트워크 케이블 최소 길이 등.
- naive: 모든 spanning tree 탐색. 지수 개 존재 (Cayley’s formula: N 개 정점 완전 그래프는 N^(N-2) 개 spanning tree).
- Kruskal: 간선을 오름차순 정렬, 사이클 안 생기면 추가. O(E log E).
- Prim: 정점 하나에서 시작, 매번 최소 가중치 간선으로 확장. O((E + V) log V) with min-heap.
핵심 통찰: cut property (자른 두 집합 연결하는 최소 간선은 MST 에 포함) 와 cycle property (사이클 내 최대 간선은 MST 에 불필요).
시각화
핵심 아이디어
Cut Property
그래프를 S 와 V \ S 로 나누는 cut 에서, 두 집합 연결하는 간선 중 최소 가중치 간선 은 어떤 MST 에든 포함.
증명: MST T 가 그 간선 e 를 안 쓰면, T 의 S—(V\S) 경로의 어떤 간선 e’ 를 e 로 교체해도 더 작거나 같은 가중치. e 가 최소이므로 w(e) ≤ w(e’), 따라서 T 는 최소가 아니거나 e 를 포함 가능.
Cycle Property
사이클에서 최대 가중치 간선 은 MST 에 포함 안 됨.
증명: 사이클 내 최대 간선 e 를 빼도 여전히 연결됨. e 를 빼고 다른 간선으로 연결하면 가중치 합 감소 또는 동일.
Kruskal 알고리즘
- 모든 간선을 가중치 오름차순 정렬.
- 간선 하나씩 보면서, 사이클을 만들지 않으면 추가.
- V - 1 개 간선 추가되면 종료.
사이클 여부는 Union-Find 로 O(α(V)) (amortized 상수).
Kruskal(G):
T = ∅
sort edges by weight
for each edge (u, v, w) in sorted order:
if find(u) != find(v):
union(u, v)
T = T ∪ {(u, v)}
return T
Prim 알고리즘
- 임의 정점 s 선택, S = {s}.
- S 와 V \ S 를 잇는 최소 가중치 간선 (u, v) 선택 (u ∈ S, v ∉ S).
- v 를 S 에 추가, (u, v) 를 MST 에 추가.
- |S| = V 까지 반복.
min-heap 으로 O((E + V) log V).
Prim(G, s):
dist[v] = ∞ for all v
dist[s] = 0
pq = {(0, s)}
T = ∅
while pq not empty:
(d, u) = pq.pop()
if u already visited: continue
mark u as visited
if parent[u] exists: T = T ∪ {(parent[u], u)}
for each (u, v, w):
if dist[v] > w and v not visited:
dist[v] = w
parent[v] = u
pq.push((w, v))
return T
구현
Kruskal (Union-Find)
// Kruskal, Union-Find
#include <bits/stdc++.h>
using namespace std;
struct Edge { int u, v, w; };
int parent[100001];
int find(int x) { return parent[x] == x ? x : parent[x] = find(parent[x]); }
bool unite(int x, int y) {
x = find(x); y = find(y);
if (x == y) return false;
parent[x] = y; return true;
}
int main() {
int V, E; cin >> V >> E;
vector<Edge> edges(E);
for (auto& [u, v, w] : edges) cin >> u >> v >> w;
sort(edges.begin(), edges.end(), [](auto& a, auto& b) { return a.w < b.w; });
for (int i = 1; i <= V; i++) parent[i] = i;
long long total = 0; int cnt = 0;
for (auto [u, v, w] : edges) {
if (unite(u, v)) {
total += w;
if (++cnt == V - 1) break;
}
}
cout << total << "\n";
}4 5
1 2 2
1 3 3
2 3 1
2 4 5
3 4 47Prim (priority_queue)
// Prim, min-heap
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
int main() {
int V, E; cin >> V >> E;
vector<vector<pii>> adj(V + 1);
for (int i = 0; i < E; i++) {
int u, v, w; cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
priority_queue<pii, vector<pii>, greater<pii>> pq;
vector<bool> vis(V + 1, false);
pq.push({0, 1});
long long total = 0;
while (!pq.empty()) {
auto [w, u] = pq.top(); pq.pop();
if (vis[u]) continue;
vis[u] = true;
total += w;
for (auto [v, ww] : adj[u]) {
if (!vis[v]) pq.push({ww, v});
}
}
cout << total << "\n";
}4 5
1 2 2
1 3 3
2 3 1
2 4 5
3 4 47복잡도
| 알고리즘 | 시간 | 공간 | 비고 |
|---|---|---|---|
| Kruskal | O(E log E) | O(V) | 간선 정렬 + Union-Find |
| Prim (heap) | O((E + V) log V) | O(V + E) | 밀집 그래프에 유리 (E ≈ V^2) |
| Prim (Fibonacci heap) | O(E + V log V) | O(V) | 이론상, 실무는 binary heap |
E = O(V^2) 이면 Kruskal O(V^2 log V), Prim O(V^2 log V) 비슷. E = O(V) 이면 Kruskal 이 빠름.
변형 / 활용
최대 신장 트리 (Maximum Spanning Tree)
간선 가중치 음수로 뒤집거나, 정렬 역순.
두 번째 MST
MST T 의 각 간선 e 를 빼고 다시 MST 구하기. O(V (E log E)).
MST 유일성
가중치가 모두 다르면 MST 유일. 같은 가중치가 있으면 여러 MST 가능. 정렬 안정성 (stable sort) 으로 사전순 최소 MST.
Borůvka 알고리즘
병렬화 가능. 각 컴포넌트가 동시에 최소 간선 선택. O(E log V), 실무는 거의 안 씀.
함정
1. 방향 그래프
MST 는 무방향 그래프 전용. 방향 그래프는 최소 신장 아보레센스 (Minimum Spanning Arborescence), Edmonds’ algorithm O(VE).
2. 연결되지 않은 그래프
컴포넌트가 여러 개면 MST 존재 안 함. 최소 신장 숲 (Minimum Spanning Forest) 으로 일반화.
3. Kruskal 에서 정렬 누락
sort(edges) 빼먹으면 greedy 보장 안 됨.
4. Prim 에서 중복 push
vis[u] 체크 안 하면 같은 노드 여러 번 pop, 시간 초과.
5. 가중치 오버플로우
int 합이 넘으면 long long.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 1197 | 최소 스패닝 트리 | - | kokoa-lab |
| BOJ 1922 | 네트워크 연결 | - | kokoa-lab |
| BOJ 4386 | 별자리 만들기 | - | kokoa-lab |
| BOJ 1774 | 우주신과의 교감 | - | kokoa-lab |
| BOJ 6497 | 전력난 | - | kokoa-lab |
💬 댓글