【题目来源】
https://www.luogu.com.cn/problem/P8655
【题目描述】
小明的实验室有 N 台电脑,编号 1∼N。原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了 BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
【输入格式】
第一行包含一个整数 N。
以下 N 行每行两个整数 a 和 b,表示 a 和 b 之间有一条数据链接相连。
输入保证合法。
【输出格式】
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
【输入样例】
5
1 2
3 1
2 4
2 5
5 3
【输出样例】
1 2 3 5
【说明/提示】
对于 30% 的数据,1≤N≤1000。
对于 100% 的数据,1≤N≤10^5,1≤a,b≤N。
时限 1 秒, 256M。蓝桥杯 2017 年第八届国赛。
【算法分析】
● 众所周知,树上没有环。一棵树由 n 个结点及 n−1 条边构成。
● 基环树是由 n 个结点及 n 条边组成的连通图。
显然,基环树上存在环。因此,基环树本质上不是树,而是图。基环树又称章鱼图。
基环树的的特别之处就在于这个环,因此,大部分基环树题目中,找环是十分必要的。
(1)“简单图”找环代码
void getloop(int u) {
dep[u]=dep[fa[u]]+1;
for(auto t:e[u]) {
if(t==fa[u]) continue;
if(!dep[t]) {
fa[t]=u;
getloop(t);
} else if(dep[t]<dep[u]) {
int i=u;
while(1) {
st[i]=1;
//loop[++idx]=i;
if(i==t) break;
i=fa[i];
}
}
}
}
(2)“重边图”找环代码
void getloop(int u,int v) {
dep[u]=dep[fa[u]]+1;
for(auto t:e[u]) {
int fi=t.first,se=t.second;
if(se==v) continue;
if(!dep[fi]) {
fa[fi]=u;
getloop(fi,se);
} else if(dep[fi]<dep[u]) {
int i=u;
while(1) {
st[i]=1;
//loop[++idx]=i;
if(fi==v) break;
i=fa[i];
}
}
}
}
● 有向基环树
有向基环树分为内向基环树和外向基环树。
内向基环树,每个结点出度为 1;外向基环树,每个结点入度为 1。
● 基环树森林
多棵树可以组成一个森林,多棵基环树可以组成基环树森林。
多棵外向基环树可以组成外向基环树森林,多棵内向基环树可以组成内向基环树森林。
【算法代码】
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int dep[N],fa[N];
bool st[N];
vector<int> e[N];
int x,y;
int n;
void getloop(int u) {
dep[u]=dep[fa[u]]+1;
for(auto t:e[u]) {
if(t==fa[u]) continue;
if(!dep[t]) {
fa[t]=u;
getloop(t);
} else if(dep[t]<dep[u]) {
int i=u;
while(1) {
st[i]=1;
if(i==t) break;
i=fa[i];
}
}
}
}
int main() {
cin>>n;
for(int i=1; i<=n; i++) {
cin>>x>>y;
e[x].push_back(y); //emplace_back
e[y].push_back(x);
}
getloop(1);
for(int i=1; i<=n; i++) {
if(st[i]) cout<<i<<" ";
}
return 0;
}
/*
in:
5
1 2
3 1
2 4
2 5
5 3
out:
1 2 3 5
*/
【参考文献】
https://www.cnblogs.com/pure4knowledge/p/18211724
https://www.cnblogs.com/yifan0305/p/17506679.html
https://blog.csdn.net/ZhuRanCheng/article/details/131736222