并查集的 unite 返回“是否发生有效合并”,这一信息正好用来检测环。
题目
一个
即这条边的两个端点在此前已经连通。请找出这条使图成环的边。
数据保证:前
输入格式
- 第一行一个整数
; - 接下来
行,每行两个整数 ,表示依次加入的边,保证无重边。
输出格式
- 输出一行两个整数
,表示使图成环的那条边(按输入中的顺序,先出现的端点在前)。
样例
样例输入
input
5
1 2
2 3
3 4
4 5
1 4样例输出
output
1 4样例解释
- 前四条边
1-2, 2-3, 3-4, 4-5把 5 个节点连成一棵树; - 加入
1-4时, 和 已经通过 连通,这条边使环 出现; - 答案唯一,输出
1 4。
提示
逐条读入并尝试合并:若两个端点已在同一集合,当前边就是答案。
题解
点击查看题解
核心思路
按输入顺序逐条加边并维护并查集。对每条边
- 若
find(u) != find(v),合并两个集合,继续; - 若
find(u) == find(v),说明 已经连通,当前边在图中形成了环,即为答案。
由于前
复杂度分析
- 时间复杂度:
- 空间复杂度:
点击查看参考代码
cpp
#include <iostream>
#include <vector>
using namespace std;
struct DSU {
vector<int> fa;
DSU(int n) {
fa.resize(n + 1);
for (int i = 1; i <= n; ++i) fa[i] = i;
}
int find(int x) {
int r = x; // 先找到根
while (fa[r] != r) r = fa[r];
while (fa[x] != r) { // 再沿途压缩
int nxt = fa[x];
fa[x] = r;
x = nxt;
}
return r;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
DSU dsu(n);
for (int i = 0; i < n; ++i) {
int u, v;
cin >> u >> v;
if (dsu.find(u) == dsu.find(v)) { // 两端已连通 -> 成环
cout << u << " " << v << '\n';
return 0;
}
dsu.fa[dsu.find(u)] = dsu.find(v); // 否则正常合并
}
return 0;
}本地运行与提交
powershell
pnpm lab:run -- labs/chapter-05/exercise/E-05-23-redundant-connection