有向图无向图判断有环(求环的长度)

东北地区赛有个无向图判自环的题当时没苟出来,回来之后想了想用并查集可以实现,然后仔细查了一波还有哪些方法……

总结一波博客吧QAQ……

资源来源:

https://blog.csdn.net/ouyangruo/article/details/51057409

https://blog.csdn.net/acmdream/article/details/72983715

http://www.xuebuyuan.com/2229565.html

https://blog.csdn.net/lezg_bkbj/article/details/11299335

 

有向图:

法一(最佳):拓扑排序

每次找到入度为0的点,按顺序将其标记,若同时有多个则无所谓其间的先后顺序。

然后将这个点删掉,将以其为起点的边都删掉,即修改那些终点的点的入度即可。

然后,如果最后修改完所有的点的入度都是0,则没有环,否则有环。

想想一个1->2->3->1的有向图中的环,这样会导致1、2、3三个点的入度同时为1,找不到入度为0的点。

 

法二:

//似乎2600是邻接矩阵的极限了

//注意到,对于每个点i,i搜索完才会vis[i]=1,在此之前vis[i]=-1

//因此,vis[i]=-1的话,如果有环就会搜到自己从而判YES,如果搜到树的叶子结点,就会把树根改为vis[i]=1,继续搜别的

#include<cstdio>  
#include<cstring>  
int n,c,a[2600][2600],vis[2600];  
int dfs(int v){      
    vis[v] = -1;      
    for(int i = 1; i <= n; i++)      
    {          
        if(a[v][i] != 0 && !vis[i])  {            
            dfs(i);          
            vis[i]=1;  
        }  
        if(a[v][i] != 0 && vis[i] == -1){       
            return 1*puts("YES");      
        }  
    }      
    return 0*puts("NO");  
}

无向图:

法一:

//用父节点和子节点的转移 注意无向图父子节点是互通的

//记互通的这条边为e 那么子节点如果通过非e的边访问到“父节点”,说明有自环

//注意“父节点”可以不是直接基类,也可以是间接基类,即之前访问过的点,vis[i]=1的点

//无向无环图可以变成一棵树,理解成父向子的递归查询即可,返祖即是有环

#include <iostream>  
using namespace std;  
const int M=2005;  
bool  g[M][M],vis[M],flag;
//无权邻接矩阵 直接判有没有相连  
int   n,m;    
bool dfs(int i,int pre)  
{  
    vis[i]=1;  
    for(int j=1;j<=n;j++)  
      if(g[i][j])  
        {  
            if(!vis[j])return dfs(j,i);  //父节点与子节点同真假  
            else if(j!=pre)return false;   //如果访问过,且不是其父节点(无向图,子节点与父节点互通),那么就构成环 
        } 
}  

法二://HDU1272 小希的迷宫 补题

①判是否有自环,只需在合并两点之前判这两点是否已经来自同一个连通的集合

②判是否是连通图,只需看://这里采用的是(2)

(1)是否只有一个根节点,即是否能转化为一棵树

(2)在没有自环的情况下,边数=点数-1(树的定义)

#include <cstdio>
#include <cstring>
int par[100005];             //记录父节点
bool used[100005];         //是否使用过
void init()
{
  for(int i=1;i<100005;++i)
  par[i]=i;
}
int find(int x)
{
  return x==find(x)?x:par[x]=find(par[x]);
}
void unit(int x,int y)
{
  x=find(x);
  y=find(y);
  if (x==y)return;
  par[y]=x;
}
bool same(int x,int y)
{
  return find(x)==find(y);
}
int main()
{
   int n,m,flag,i,t;
   while (scanf("%d %d",&n,&m)!=EOF)
   {
    if (n==-1 && m==-1)break;
    if (n==0 && m==0)                      //当一开始就输入0 0的话,要输出Yes,本题坑爹处
    {
         printf ("Yes\n");
         continue;
    }
   memset(used,0,sizeof(used));
   init();
   unit(n,m); 
   used[n]=1,used[m]=1;//合并后,说明这两个点用过了
   t=1;                                              //t=点-边   刚开始时是2-1=1
   flag=1;                                           //flag表示当前所有点的根是否唯一,换言之,图是否是连通图
   while (scanf("%d %d",&n,&m)!=EOF)                 //核心部分,相当于模拟加边的过程
   {
    if (n==0 && m==0)break;
    if (used[n]==0)t++,used[n]=1;//加入一点,并标记
    if (used[m]==0)t++,used[m]=1;
    if (same(n,m))flag=0;                            //n与m在连这条边之前就已经连通
    else t--;                                        //若n和m的根节点不相同,则将他们合并,并且边数+1,相当于t-1
   }
   if (flag&&t==1)puts("Yes");                     //若图中没有环,而且点数与边数相差1,则说明该图是符合的!
   else puts("No");
   }
    return 0;
}

求无向图环的长度

以cf263D为例,求出一个长度不短于k+1的环

考虑dfs序,dfs完退栈

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,k;
int dfn[N],in[N],cnt;
int res[N],tot;
vector<int>E[N];
bool ok;
void dfs(int u,int fa)
{
	if(ok)return;
	in[u]=++cnt;
	dfn[cnt]=u;
	for(int v:E[u])
	{
		if(v==fa||in[v]==-1)continue;
		if(!in[v])dfs(v,u);
		else
		{
			if(ok)return;
			if(in[u]-in[v]>=k)
			{
				for(int j=in[v];j<=in[u];++j)
				res[++tot]=dfn[j];
				ok=1;
				return;
			}
		}
	}
	cnt--;
	in[u]=-1;
}
int main()
{
	int u,v;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&u,&v);
		E[u].push_back(v);
		E[v].push_back(u);
	}
	dfs(1,-1);
	printf("%d\n",tot);
	for(int i=1;i<=tot;++i)
	printf("%d%c",res[i]," \n"[i==tot]);
	return 0;
}

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值