题干:
问题描述
小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入格式
第一行包含一个整数N。
以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
对于30%的数据,1 <= N <= 1000
对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N
输入保证合法。
输出格式
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
样例输入
5
1 2
3 1
2 4
2 5
5 3
样例输出
1 2 3 5
解题报告:
法一:先并查集求出第一个 成环 的边,这条边的两个顶点一定是环中的顶点,以这两个点为起点进行dfs,找到环并记录,就可以了。
法二:tarjan。
法三:记录每个顶点的入度,用类似拓扑排序的方法处理所有顶点,如果最后queue内没有元素了的时候,剩下没有遍历到的元素都是环内元素,时间关系不再实现代码。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
vector<int> vv[MAX];
int f[MAX];
int ans[MAX];
int tar1,tar2,flag,tot;
int getf(int v) {
return f[v] == v ? v : f[v] = getf(f[v]);
}
void merge(int u,int v) {
int t1 = getf(u);
int t2 = getf(v);
f[t2]= t1;
}
void dfs(int cur,int rt,int step) {
int up = vv[cur].size();
if(flag) return ;
ans[step] = cur;
if(cur == tar2) {
tot = step;
flag = 1;
return;
}
for(int i = 0; i<up; i++) {
int v = vv[cur][i];
if(v == rt) continue;
dfs(v,cur,step+1);
if(flag) return ;
}
}
int main()
{
int n,fg=0;
cin>>n;
for(int i = 1; i<=n; i++) f[i] = i;
for(int u,v,i = 1; i<=n; i++) {
scanf("%d%d",&u,&v);
vv[u].pb(v);
vv[v].pb(u);
if(getf(u) == getf(v)&&!fg) {
tar1=u,tar2=v;
fg=1;
}
merge(u,v);
}
dfs(tar1,tar2,1);
sort(ans+1,ans+tot+1);
for(int i = 1; i<=tot; i++) printf("%d%c",ans[i],i==tot?'\n':' ');
return 0 ;
}
Tarjan写法:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<stack>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
vector<int> vv[MAX];
int n,m;
int DFN[MAX],LOW[MAX],stk[MAX];
bool vis[MAX];
int f[MAX];
stack<int> sk;
int ans[MAX],fk;
int clk,index;
void tarjan(int x,int rt) {
DFN[x] = LOW[x] = ++clk;
vis[x] = 1;
stk[++index] = x;
int up = vv[x].size();
for(int i = 0; i<up; i++) {
int v = vv[x][i];
if(v == rt) continue;
if(!DFN[v]) {
tarjan(v,x);
LOW[x] = min(LOW[x],LOW[v]);
} else{
if(vis[v]) LOW[x] = min(LOW[x],DFN[v]);
else printf("123123");
}
}
if(DFN[x] == LOW[x]) {
while(sk.size()) sk.pop();
while(1) {
int tmp = stk[index--];
sk.push(tmp);
vis[tmp]=0;
if(tmp == x) break;
}
if(sk.size() > 1) {
while(sk.size()) ans[++fk] = sk.top(),sk.pop();
}
}
}
int main() {
cin>>n;
for(int i = 1; i<=n; i++) f[i]=i;
for(int a,b,i = 1; i<=n; i++) {
scanf("%d%d",&a,&b);
vv[a].pb(b);
vv[b].pb(a);
}
tarjan(1,-1);
sort(ans+1,ans+fk+1);
for(int i = 1; i<=fk; i++) printf("%d%c",ans[i],i == fk ? '\n' : ' ');
return 0 ;
}
因为可以保证只有一个环,所以只会有一次机会更新ans数组。