給定 N 個城鎮(節點)和 M 條現有道路(無向邊),道路可能重複。目標:使任意兩城鎮連通(直接或間接)。**求最少還需建設多少條道路?**
並查集 是解決動態連通性問題的首選數據結構,支持:
Find(x):查找 x 所在集合代表元(路徑壓縮優化)Union(x, y):合併兩集合(按秩/大小優化)#include
using namespace std;
class DSU {
vector parent, rank;
public:
DSU(int n = 0) : parent(n+1), rank(n+1, 0) {
iota(parent.begin(), parent.end(), 0); // parent[i] = i
}
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]); // 路徑壓縮
}
void unite(int x, int y) {
x = find(x); y = find(y);
if (x == y) return;
if (rank[x] < rank[y]) swap(x, y);
parent[y] = x;
if (rank[x] == rank[y]) rank[x]++;
}
int countComponents(int n) {
int cnt = 0;
for (int i = 1; i <= n; ++i)
if (find(i) == i) cnt++;
return cnt;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, M;
while (cin >> N >> M && N) {
DSU dsu(N);
for (int i = 0, a, b; i < M; ++i) {
cin >> a >> b;
dsu.unite(a, b); // 重複邊自動去重
}
int components = dsu.countComponents(N);
cout << components - 1 << '\n';
}
return 0;
}
適用於邊數較稠密或需輸出具體連通分量成員時:
// DFS 版本
vector> adj;
vector vis;
void dfs(int u) {
vis[u] = true;
for (int v : adj[u])
if (!vis[v]) dfs(v);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, M;
while (cin >> N >> M && N) {
adj.assign(N+1, {});
vis.assign(N+1, false);
for (int i = 0, a, b; i < M; ++i) {
cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
int comp = 0;
for (int i = 1; i <= N; ++i)
if (!vis[i]) { dfs(i); comp++; }
cout << comp - 1 << '\n';
}
}
| 算法 | 時間複雜度 | 空間複雜度 | 適用場景 |
|---|---|---|---|
| 並查集 | O(M α(N)) ≈ O(M) | O(N) | 海量數據、邊動態增刪、純連通性查詢 |
| DFS (遞歸) | O(N + M) | O(N + M) + 遞歸棧 | 需遍歷圖結構、邊稠密、N < 10^5 避免棧溢出 |
| BFS (隊列) | O(N + M) | O(N + M) | 避免遞歸棧溢出、需最短路徑擴展 |
本題 N < 1000,兩種方法均輕鬆通過。**並查集代碼更短、常數更小、無遞歸風險,推薦首選。**
N=0 終止,**該用例不處理**Huge input, scanf is recommended,C++ 用 ios::sync_with_stdio(false); cin.tie(nullptr); 同等效果4 2 -> 城鎮 1,2,3,4;道路 1-3, 4-3 -> 分量: {1,3,4}, {2} -> 2-1=1 ✓
3 3 -> 完全圖 -> 1 分量 -> 0 ✓
5 2 -> 1-2, 3-5 -> 分量: {1,2}, {3,5}, {4} -> 3-1=2 ✓
999 0 -> 999 孤立點 -> 999-1=998 ✓
核心洞見: 「最少新建道路數 = 連通分量數 - 1」是圖論中**連通分量與生成樹關係**的直接推論。掌握並查集,即掌握了解決一大類動態連通性問題的通用鑰匙。