首先总结一下判环的方法:
对于普通的判环,可以用并查集来做;
判奇偶环有两种方法:一是二分图的染色判定,二也是通过并查集,不过是带权并查集
首先给出二分图染色判定的:
//二分图染色判断(二分图一定不存在奇环 非二分图一定存在奇环)
//存在返回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