hdu5215 dfs染色判奇环+边双连通分量判偶环 (并查集)

Cycle

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 131    Accepted Submission(s): 39


Problem Description
Ery is interested in graph theory, today he ask BrotherK a problem about it: Given you a undirected graph with  N  vertexes and  M  edges, you can select a vertex as your starting point, then you need to walk in the graph along edges. However, you can't pass a edge more than once, even opposite direction is forbidden. At the end, you should come back to the starting point. Assume you has passed  X  edges, there are two questions:

Question 1: Can  X  be a odd number ?

Question 2: Can  X  be a even number ?

(note: you must walk, so  X  can't be 0)
 

Input
The first line contains a single integer  T , indicating the number of test cases.

Each test case begins with two integer  N, M , indicating the number of vertexes and the number of edges. Following  M  lines, each line contains two integers  Ui, Vi , indicating there are a edge between vertex  Ui  and vertex  Vi .

T  is about 30

1  N  100000

0  M  300000

1  Ui,Vi  N

Ui  will not equal to  Vi

There is at most one edge between any pair of vertex.
 

Output
For each test, print two lines.

The first line contains "YES" or "NO" for question 1. 

The second line contains "YES" or "NO" for question 2.
 

Sample Input
  
  
3 1 0 3 3 1 2 2 3 3 1 4 4 1 2 2 3 3 4 4 1
 

Sample Output
  
  
NO NO YES NO NO YES
Hint
If you need a larger stack size, please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.
 

Source


题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5215

题意:给你一个无向图,从一个点出发然后回到该点,且每条边只能走一次,问是否存在奇环和偶环。

思路:判断奇环,可以用dfs染色判断是不是二分图,二分图中必定不存在奇环,而非二分图中必定存在奇环。

判断偶环,可以找出该图的双连通分量是否只存在一个单独的奇环,否则必定存在偶环,因为由多个环组成的双连通分量必定存在偶环。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0xfffffff
#define MAX 1000005
#define mod 10000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
struct edge {
    int v,next;
} e[MAX*4];
int pre[MAX],low[MAX],bcc_cnt=0,dfs_clock=0;
int head[MAX],bridge[MAX*4],tot,vis[MAX],color[MAX];
void addedge(int u,int v) {
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
int odd=0,even=0;
void dfs(int u) {
    if(odd) return ;
    for(int i=head[u]; i!=-1; i=e[i].next) {
        int v=e[i].v;
        if(color[v]==-1) {
            color[v]=(color[u]+1)%2;
            dfs(v);
        } else if(color[v]==color[u]) {
            odd=1;
            return ;
        }
    }
}
void tdfs(int u,int fa) {
    pre[u]=low[u]=++dfs_clock;
    for(int i=head[u]; i!=-1; i=e[i].next) {
        int v=e[i].v;
        if(!pre[v]) {
            tdfs(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>pre[u]) {
                bridge[i]=bridge[i^1]=1;
            }
        } else if(pre[v]<pre[u]&&v!=fa)
            low[u]=min(low[u],pre[v]);
    }
}
void find_bcc(int n) {
    CLR(pre,0);
    CLR(low,0);
    for(int i=1; i<=n; i++) {
        if(!pre[i]) {
            tdfs(i,-i);
        }
    }
}
int v_cnt,e_cnt;
void dfs2(int u) {
    vis[u]=1;
    v_cnt++;
    for(int i=head[u]; i!=-1; i=e[i].next) {
        if(bridge[i]) continue;
        int v=e[i].v;
        e_cnt++;
        if(!vis[v]) {
            dfs2(v);
        }
    }

}
void init() {
    tot=0;
    CLR(head,-1);
    CLR(color,-1);
    CLR(vis,0);
    CLR(bridge,0);
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        init();
        int n,m,u,v;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++) {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        odd=even=0;
        for(int i=1; i<=n; i++) {
            if(color[i]==-1) {
                color[i]=0;
                dfs(i);
                if(odd) break;
            }
        }
        find_bcc(n);
        for(int i=1; i<=n; i++) {
            v_cnt=e_cnt=0;
            if(!vis[i]) {
                dfs2(i);
            }else continue;
            if(v_cnt==1) continue;
            e_cnt/=2;
            if(!(e_cnt==v_cnt&&v_cnt%2==1)) {
                even=1;
                break;
            }
        }
        if(odd) printf("YES\n");
        else printf("NO\n");
        if(even) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


并查集:

将u,v 两点拆成2u,2u+1,v,2v+1;如果2u和2v+1已经相连说明存在偶环,否则2u与2v+1相连,2u+1与2v相连,每次连接后发现2u和2u+1相连说明存在奇环。

证明:将点拆分然后交叉相连,点u经过奇数次回到u,会导致自己两个点回到一个集合,如果经过偶数次回到u,这是就会发现,要连接的时候2u,已经和2v+1在同一个集合了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0xfffffff
#define MAX 1000005
#define mod 10000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
int pre[2*MAX];
int find(int r) {
    while(r!=pre[r]) {
        r=pre[r]=pre[pre[r]];
    }
    return r;
}
int even,odd;
void add(int u,int v) {
    int a=find(u);
    int b=find(v);
    if(a!=b) {
        pre[a]=b;
    }
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        int n,m,u,v;
        scanf("%d%d",&n,&m);
        odd=0,even=0;
        for(int i=1;i<=2*(n+1);i++) pre[i]=i;
        for(int i=1; i<=m; i++) {
            scanf("%d%d",&u,&v);
            if(odd&&even) continue;
            if(find(2*u)==find(2*v+1)) even=1;
            add(2*u,2*v+1);
            add(2*u+1,2*v);
            if(find(u*2)==find(u*2+1)) odd=1;
        }
        if(odd) printf("YES\n");
        else printf("NO\n");
        if(even) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值