有向图判环HDU3342 无向图判环HDU1272

14 篇文章 0 订阅
  • 判环的思路

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;

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值