历届试题 发现环
时间限制: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; }