历届试题 发现环
时间限制:1.0s 内存限制:256.0MB
问题描述
小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入格式
第一行包含一个整数N。
以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
对于30%的数据,1 <= N <= 1000
对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= 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 1
2 4
2 5
5 3
样例输出
1 2 3 5
方法一:dfs
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
using namespace std;
const int N = 100000+5;
int n;
bool vis[N];
vector<int> adj[N];
bool in_stack[N];
bool flag[N];
stack<int> s;
void dfs(int cur,int fa)
{
vis[cur] = true;
s.push(cur);
in_stack[cur] = true;
for(int i = 0; i < adj[cur].size(); ++i){
int v = adj[cur][i];
if(!vis[v]){
dfs(v,cur);
}
else if(in_stack[v] && fa != v){//不能是父节点
int u;
do{
u = s.top();
flag[u] = true;
s.pop();
}while(u != v);
//return; // 不知道为啥 不能 直接return;
break;
}
}
if(!s.empty()) //这里如果不判断 直接出栈 会RE 从 100 -> 0 悲剧
s.pop();
in_stack[cur] = false; //出栈了必须标记
}
int main()
{
scanf("%d",&n);
int a,b;
for(int i = 0; i < n; ++i){
scanf("%d%d",&a,&b);
adj[a].push_back(b);
adj[b].push_back(a);
}
dfs(1,1); //图一定连通 所以 从一个点搜索就OK了
// for(int i = 1; i <= n; ++i){
// if(!vis[i]){
// dfs(i,i);
// }
// }
for(int i = 1; i <= n; ++i){
if(flag[i]){
printf("%d ",i);
}
}
return 0;
}
方法二:tarjan变形(原算法主要针对有向图) 这里根据求割点的方法变形
关键在于:对顶点u 其子树经过
非父子边所能回到的最早的时间戳
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int N = 100000+5;
int n;
bool vis[N];
vector<int> adj[N];
bool in_stack[N];
bool flag[N];
stack<int> s;
int index;
int low[N];
int dfn[N];
int color;
vector<int> cop[N];//记录每个连通分量 其实只有一个
void dfs(int cur,int fa)
{
dfn[cur] = low[cur] = ++index;
vis[cur] = true;
s.push(cur);
in_stack[cur] = true;
for(int i = 0; i < adj[cur].size(); ++i){
int v = adj[cur][i];
if(!vis[v]){
dfs(v,cur);
low[cur] = min(low[cur],low[v]);
}
else if(in_stack[v] && v != fa){ //这是关键 不能是父节点 类似于判断割点
low[cur] = min(low[cur],dfn[v]);
}
}
in_stack[cur] = false;
if(low[cur] == dfn[cur]){ //说明是个连通分量
int u;
++color;
do{
u = s.top();
cop[color].push_back(u);
s.pop();
}while(u != cur);
}
}
int main()
{
scanf("%d",&n);
int a,b;
for(int i = 0; i < n; ++i){
scanf("%d%d",&a,&b);
adj[a].push_back(b);
adj[b].push_back(a);
}
dfs(1,1); //图一定连通 所以 从一个点搜索就OK了
for(int i = 0; i < N; ++i){
if(cop[i].size() > 1){
for(int j = 0; j < cop[i].size(); ++j){
sort(cop[i].begin(),cop[i].end());//排序
printf("%d ",cop[i][j]);
}
}
}
return 0;
}