题干:

问题描述

  小明的实验室有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数组。