题干:
问题描述
小明的实验室有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数组。