-
判环的思路
HDU3342 有向图判环,可以采用拓扑排序,DFS。
HDU1272 无向图判环,可以采用并查集。
使用拓扑排序判断有向环,就是在拓扑排序结束后,判断排序了几个节点(cnt),如果正好是图的节点数(n),cnt==n时,图中不存在环。
使用DFS判断有向环,需要分层来考虑。在对当前的节点进行搜索时,当前节点的vis[start]=1,表示正在搜索该节点。vis[ i ]==0表示该节点还没有被访问过,vis[ i ]== -1 表示当前节点已经被访问了。 对当前节点的邻接节点进行搜索,如果发现==0,递归进入下一层dfs, 如果发现==1,表示绕了一圈又回来了,直接改变flag,返回。 搜索结束对当前节点置- 1,访问完毕。
使用并查集判断无向图的环,如果无向图中有环存在,那么 pre[ i ] == i 的个数必定大于1个。 或者,如果没有环存在,所有结点的父节点都是一个,find(i)==find(n) (pre[ i ] == pre[ n ]不能判断,会出问题的,,可能有多个并查集)。
-
有向图判环 HDU3342
题意:一群大牛互相问问题,大牛有不会的,会被更厉害的大牛解答,更厉害的大牛是会的东西比大牛多,但是有的时候更厉害的大牛会装弱,出来问问题,这样就被大牛解答了。这样就形成了一个圈。题目的意思就是让你在一个有向图里面判断是否存在环。我们可以通过dfs和拓扑排序两种方法。
-
有向图------DFS方案
const int maxn=105;
int mp[maxn][maxn];
int vis[maxn];
bool flag;
int n,m;
void dfs(int start)
{
if(flag) return ;
vis[start]=1;// 当前正在访问
for(int i=0;i<n;i++)
{
if(vis[i]==0 && mp[start][i] )//没有被访问过的,相邻节点
{
// cout<<"start: "<<start<<" dfs(i):"<<i<<endl;
dfs(i);//没有访问过
}
else if(vis[i]==1 && mp[start][i])//有环存在的条件:从其他地方的相邻节点,结果又找到了自己
{
// cout<<"start: "<<start<<" i:"<<i<<endl;
flag=true;
return ;
}
}
vis[start]=-1;//已经访问过了
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF )
{
if(n==0 && m == 0) return 0;
memset(mp,0,sizeof(mp));
memset(vis,0,sizeof(vis));
flag=false;
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
mp[a][b]=1;
}
for(int i=0;i<n;i++)
{
if(flag) break;
if(!vis[i])
{
dfs(i);
}
}
if(!flag) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
由于使用的mp二维数组进行存储,所以在dfs时,判断邻接节点很麻烦,需要搜索一遍。
所以,也有大佬使用 vector<int> vect[maxn]
vector<int> vec[maxn];
int vis[maxn]; //0表示该顶点未访问,1表示该顶点正在被访问,-1表示该顶点访问已经完成
bool flag;
void dfs(int x)
{
if(flag){
return;
}
vis[x] = 1;
for(int i=0;i<sz(vec[x]);i++)
{
if(vis[vec[x][i]] == 0) dfs(vec[x][i]);
else if(vis[vec[x][i]] == 1){
flag = true;
return;
}
}
vis[x] = -1;
}
---------------------
作者:WangMeow
来源:CSDN
原文:https://blog.csdn.net/m0_37624640/article/details/82948895
版权声明:本文为博主原创文章,转载请附上博文链接!
-
有向图------拓扑排序
正常的拓扑排序。时刻要注意,拓扑排序,,可能会出现重边!代码里,借的先判断,再加入度。
//HDU 3342
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<stdio.h>
using namespace std;
#define ll long long
const int maxn=105;
int mp[maxn][maxn];
int indegree[maxn];
int vis[maxn];
int n,m;
int topo()
{
queue<int> qu;
int cnt=0;
for(int i=0;i<n;i++)
{
if(!indegree[i])
{
qu.push(i);
vis[i]=1;
}
}
while(!qu.empty())
{
int t=qu.front();
// cout<<"pop:"<<t<<endl;
qu.pop();
cnt++;
for(int i=0;i<n;i++)
{
if(mp[t][i] && !vis[i])
{
indegree[i]--;
if(!indegree[i])
{
qu.push(i);
vis[i]=1;
// cout<<"push:"<<i<<endl;
}
}
}
}
if(cnt==n) return 1;
else return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF )
{
if(n==0 && m == 0) return 0;
memset(mp,0,sizeof(mp));
memset(indegree,0,sizeof(indegree));
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(mp[a][b]) continue;//重边坑人
mp[a][b]=1;
indegree[b]++;
}
int res=topo();
if(res) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
拓扑排序时,找一个数组 vis[ ] 来记录,当前节点是否进入过队列。 如果不标记的话,该节点的入度始终为0,会造成死循环。
-
HDU 1272 无向图判环 ------ 并查集
const int maxn=100005;
int pre[maxn];
int vis[maxn];
int n;
int flag;
void Init()
{
n=0;flag=1;
memset(vis,false,sizeof(vis));
for(int i=0;i<maxn;i++)
pre[i]=i;
}
int find(int x)
{
int r=x;
while(r!=pre[r])
{
r=pre[r];
}
int j=x;
while(j!=r)
{
int t=pre[j];
pre[j]=r;
j=t;
}
return r;
}
int main()
{
int a,b;
Init();
while(scanf("%d%d",&a,&b)!=EOF)
{
if(a==-1 && b==-1)
return 0;
if(a==0 && b==0 )
{
int cnt=0;
// cout<<fa<<" ";
for(int i=1;i<=n;i++)
{
if(vis[i] && pre[i]==i)
cnt++;
}
if(cnt>1) flag=0;
if(!flag) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
Init();
continue;
}
n=max(n,max(a,b));
if(a==b) continue;
vis[a]=true;
vis[b]=true;
int x=find(a);
int y=find(b);
if(x!=y)
{
pre[y]=x;
}
else
{
flag=0;
}
}
}
或者,如果没有绕过来pre[i] == i的个数不能超过1的弯弯,你也可以这样:
int fa=find(n);
int cnt=0;
// cout<<fa<<" ";
for(int i=1;i<=n;i++)
{
if(vis[i] && find(i)!=fa)
{
// printf("find(%d)=%d\t",i,find(i));
cnt++;
}
}
if(cnt) flag=0;
if(!flag) cout<<"No"<<endl;
else cout<<"Yes"<<endl;