蓝桥杯 2017 决赛 发现环 (求环内的点)

  历届试题 发现环  
时间限制:1.0s   内存限制:256.0MB
   
问题描述
  小明的实验室有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

#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;
	}

猜你喜欢

转载自blog.csdn.net/tianweidadada/article/details/80431428