HDU3342 判断有向图中是否存在三元环 拓扑排序 tarjan

题目大意:

给你一个关系图,判断是否合法,
   每个人都有师父和徒弟,可以有很多个;
   且 若A是B的师父,B是C的师父,则A是C的师父。
   不合法的情况:
   1) 互为师徒;
   2) 你的师父是你徒弟的徒弟,或者说你的徒弟是你师父的师父。(3元环或多元环)

题目链接:点击做题

简单理解就是:

 判断有向图中是否存在至少3元环;
 也就是判断有没有回路自环啥的东西;
 此题至少有三种做法。


有向图,无向图判断环的方法及判断负环的方法:

有向图判断环的方法:
1. DFS标记白灰黑 
2. 拓扑排序 
3. Tarjan

无向图判断环的方法:
1. 改动的拓扑排序(BFS:du[i]<=1,push) 
2. DFS标记白灰黑 
3. 并查集

拓扑序和rdfs序与遍历图的顺序无关,是不变的。
一个无向完全图给部分边定向后若不存在环,则总有一种给其他边定向的方式使得这个有向图无环

有向图判断环的方法:

DFS标记白灰黑
拓扑排序
Tarjan

无向图判断环的方法:

改动的拓扑排序(BFS:du[i]<=1,push)
DFS标记白灰黑
并查集


法一:拓扑排序

1) . 统计每个点的入度;
  2) . 将入度为0的点加入队列;
  3) . 出去队首元素,将此元素所连接的点入度减一,若此后入度为0则加入队列;
  4.) 判断队列循环次数,若等于n则不存在回路,则此关系图合法;

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<cmath>  
#include<cstdlib>  
#include<queue>  
#include<map>  
#include<stack>  
#include<vector>  
#include<ctime>  
using namespace std;  
const int  N = 2005;
const int  M = 3000005;
int n,m;
int tot,flag;
int in[N],head[N];
struct lp
{
	int u,v,nex;
	lp(){}
	lp(int a,int b,int c):
	u(a),v(b),nex(c){}
}cw[N];
void add(int a,int b){
	cw[++tot]=lp(a,b,head[a]);
	head[a]=tot;
}
void tuopu(){
	queue<int>Q;
	while(!Q.empty())Q.pop();
	for(int i=0;i<n;++i){
		if(in[i]==0)Q.push(i);
	}
	int t=0;
	while(!Q.empty()){
		t++;
		int u=Q.front();Q.pop();
		for(int i=head[u];i!=-1;i=cw[i].nex){
			int v=cw[i].v;
			in[v]--;
			if(in[v]==0)Q.push(v);
		}
	}
	if(t==n)flag=1;
}
int main(int argc, char const *argv[])
{
	int a,b;
	while(~scanf("%d%d",&n,&m)&&(n)){
		memset(in,0,sizeof(in));
		tot=-1;
		memset(head,-1,sizeof(head));
		for(int i=0;i<m;++i){
			scanf("%d%d",&a,&b);
			add(a,b);
			in[b]++;
		}
		flag=0;
		tuopu();
		if(flag)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

法二:tarjan

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<cmath>  
#include<cstdlib>  
#include<queue>  
#include<map>  
#include<stack>  
#include<vector>  
#include<ctime>  
using namespace std;  
const int  N = 105;
const int  M = 3000005;
int n,tot,flag,idex;
int head[N],vis[N];
int low[N],dfn[N];
int qltNum;
int qltMap[N];
stack<int>st;
struct lp
{
	int to,nex;
	lp(){}
	lp(int a,int b):
	to(a),nex(b){}
}cw[N*N];
void add(int a,int b){
	cw[++tot]=lp(b,head[a]);
	head[a]=tot;
}
void dfs(int u,int fa){
	dfn[u]=low[u]=++idex;
	vis[u]=1;
	st.push(u);
	int v;
	for(int i=head[u];i!=-1;i=cw[i].nex){
		v=cw[i].to;
		if(v==fa){
			flag=1;
			break;
		}
		if(!vis[v]){
			dfs(v,u);
			if(flag)return;
			low[u]=min(low[u],low[v]);
			//if(low[v] > dfn[u]) bridge
		}else if(vis[v]==1){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		qltNum++;
		int t=0;
		do{
			t++;
			v=st.top();st.pop();
			vis[v]=2;
			qltMap[v]=qltNum;
			if(t>=3){
				flag=1;
				return;
			}
		}while(v!=u);
		//cout<<t<<"\n";
	}
}
void tarjan(){
	for(int i=1;i<=n;++i){
		if(!vis[i]){
			dfs(i,-1);
		}
		if(flag)return;
	}
}
void init(){
	while(!st.empty())st.pop();
	qltNum=idex=flag=0;
	tot=-1;
	memset(head,-1,sizeof(head));
	memset(vis,0,sizeof(vis));
    memset(qltMap,0,sizeof(qltMap));
}
#define DEBUG
int main(int argc, char const *argv[]) {
	int a,b,m;
	#ifdef DEBUG
    	freopen("D:in.in", "r", stdin);
   		freopen("D:out.out", "w", stdout);	
	#endif
	while(~scanf("%d%d",&n,&m)&&(n)){
		init();
		memset(head,-1,sizeof(head));
		for(int i=0;i<m;++i){
			scanf("%d%d",&a,&b);
			a++,b++;
			add(a,b);
		}
		tarjan();
		if(flag)printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值