3 1 0 3 3 1 2 2 3 3 1 4 4 1 2 2 3 3 4 4 1
NO NO YES NO NO YESHintIf you need a larger stack size, please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.
题目连接 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; }