并查集典型应用
一、连通性问题
连通性问题是并查集最基本也是最常见的应用场景。在这类问题中,我们需要判断图中的两个节点是否连通,或者维护图的连通性信息。
1.1 判断两点是否连通
最简单的连通性问题是判断两个节点是否连通。使用并查集,我们可以通过查找两个节点是否属于同一个集合来判断它们是否连通。
// 判断节点x和y是否连通
bool isConnected(int x, int y) {
return find(x) == find(y);
}
示例:洛谷P1551 亲戚
规定: 和 是亲戚, 和 是亲戚,那么 和 也是亲戚。如果 , 是亲戚,那么 的亲戚都是 的亲戚, 的亲戚也都是 的亲戚。
输入数据格式
第一行:三个整数 ,(),分别表示有 个人, 个亲戚关系,询问 对亲戚关系。
以下 行:每行两个数 ,,,表示 和 具有亲 戚关系。
接下来 行:每行两个数 ,询问 和 是否具有亲戚关系。
输出数据格式
行,每行一个 Yes 或 No。表示第 个询问的答案为“具有”或“不具有”亲戚关系。
输入输出样例
| 输入 #1 | 输出 #1 |
|---|---|
| 6 5 3 1 2 1 5 3 4 5 2 1 3 1 4 2 3 5 6 | Yes Yes No |
这道题目要求判断两个人是否有亲戚关系,可以直接使用并查集解决:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
int parent[MAXN];
int find(int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
}
void unionSets(int x, int y) {
parent[find(x)] = find(y);
}
int main() {
int n, m, p;
cin >> n >> m >> p;
// 初始化
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
// 合并亲戚关系
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
unionSets(x, y);
}
// 查询
for (int i = 0; i < p; i++) {
int x, y;
cin >> x >> y;
if (find(x) == find(y)) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
1.2 动态连通性维护
动态连通性问题是指在图中不断添加边,并查询两点是否连通的问题。并查集可以高效地维护这种动态变化的连通性信息。
示例:村村通
某市调查城镇交通状况,得到现有城镇道路统计表。表中列出了每条道路直接连通的城镇。市政府 "村村通工程" 的目标是使全市任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要相互之间可达即可)。请你计算出最少还需要建设多少条道路?