hdu5215(判环总结)

首先总结一下判环的方法:

对于普通的判环,可以用并查集来做;

判奇偶环有两种方法:一是二分图的染色判定,二也是通过并查集,不过是带权并查集

首先给出二分图染色判定的:

 

//二分图染色判断(二分图一定不存在奇环 非二分图一定存在奇环)
//存在返回true
bool judge(int u)
{
    int len=edge[u].size();
    cnt[vis[u]-1]++;
    for(int i=0;i<len;i++)
    {
        int v=edge[u][i];
        if(!vis[v])
        {
            vis[v]=3-vis[u];
            if(judge(v))return true;
        }
        else if(vis[v]==vis[u])
            return true;
    }
    return false;
}
//判断奇环
//二分图
int dfs(int x,int fa,int val)
{
	if(vis[x]==-1)val[x]=val;
	else return vis[x];
	for(int i=head[x];i!=-1;i=edge[i].next)
	{
		int v=edge[i].v;
		if(vis[x]==dfs(v,x,vis[x]^1))
			hasc=1;
	}
	return vis[x];
}


然后是并查集的判奇环:

//带全并查集
int find(int x)
{
	if(x==pre[x])return x;
	int fa=pre[x];
	pre[x]=find(pre[x]);
	val[x]^=val[fa];
	return pre[x];
}
void solve()
{
	scanf("%d",&M);
	while(M--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add_edge(x,y);
		add_edge(y,x);
		int f1=find(x),f2=find(y);
		if(f1!=f2)
		{
			pre[f1]=f2;
			val[f1]=val[x]^val[y]^1;
		}
		else if(val[x]^val[y]==0)
			hasc=1;
	}
}

最后说一下这个题是什么意思:

题意:给一个无向图,判断是不是有奇环和偶环

对于问题1,我们只需要进行二分图染色判定这个图是否是二分图即可
二分图中必定不存在奇环,而非二分图中必定存在奇环
对于问题2,首先我们注意到一个环一定存在于双联通分量(既去掉任何一条边后仍然联通的点集)内
通过tarjan算法,可以分离出所有的双联通分量,然后分别检查其中是否存在偶环
对于一个双联通分量,如果它仅仅是一个环,那么只需判断它是否是偶环即可
否则其中必定存在两个缠绕(共享至少一个点)的环,若这两个环的都是奇环,那么去掉共享的边之后可以组成一个大偶环
因此,除非一个双联通分量仅仅是一个奇环,那么其中必定存在偶环
而我们可以发现,若一个非单点的双联通分量仅仅由一个环构成,那么它的点数必定等于边数,据此判断即可
时间复杂度O(N + M)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
const int maxn=100010;
int N,M,dfs_clock;
int head[maxn];
int tot,odd,even;
struct node
{
	int v,next;
}edge[maxn*6];
int in[maxn];
int fa[maxn];
int  pre[maxn],low[maxn],vis[maxn],dfn[maxn];
int bcc_cnt;
int bridge[maxn*6];
int dep[maxn];
stack<int> S;
void init()
{
	tot=dfs_clock=bcc_cnt=0;
	memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(pre,0,sizeof(pre));
	memset(vis,0,sizeof(vis));
	memset(bridge,0,sizeof(bridge));
	memset(in,0,sizeof(in));
	while(!S.empty())S.pop();
}
void add_edge(int u,int v)
{
	edge[tot].v=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void tarjan(int u,int f)
{
	vis[u]=1;
	dfn[u]=low[u]=++dfs_clock;
	S.push(u);
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].v;
		if(!dfn[v])
		{
			pre[v]=(i^1);
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(v!=f&&dfn[v]<dfn[u])
		{
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(dfn[u]==low[u])
	{
		bcc_cnt++;
		int j;
		bridge[pre[u]]=1;
		bridge[pre[u]^1]=1;
		do
        {
            j=S.top();S.pop();
        }
        while(j!=u);
	}
}
int sum;
void dfs(int u,int deep)
{
	vis[u]=1;
	dep[u]=deep;in[u]=1;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		if(bridge[i])continue;
		int v=edge[i].v;
		if(!vis[v])
		{
			fa[v]=u;
			dfs(v,deep+1);
		}
		else if(in[v]&&v!=fa[u])
		{
			int len=deep-dep[v]+1;
			if(len&1)
			{
				odd=1;
				sum++;
				if(sum>1)even=1;
			}
			else even=1;
		}
	}
	in[u]=0;
}
int main()
{
	int T,u,v;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&N,&M);
		init();tot=2;
		odd=even=0;
		for(int i=1;i<=M;i++)
		{
			scanf("%d%d",&u,&v);
			add_edge(u,v);
			add_edge(v,u);
		}
		for(int i=1;i<=N;i++)
			if(!pre[i])tarjan(i,0);
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=N;i++)
		{
			if(!vis[i])
			{
				sum=0;
				fa[i]=0;
				dfs(i,1);
			}
		}
		printf("%s\n",odd?"YES":"NO");
		printf("%s\n",even?"YES":"NO");
	}
	return 0;
}


突然发现一个博客:直接二分图判奇偶环

http://blog.csdn.net/gg_gogoing/article/details/45456443




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值