1.无向图的欧拉回路判断:
如果一个无向图是连通的,并且每个点的度是偶数,那么这个无向图具有欧拉回路,所以
无向图的欧拉回路判断是非常简单的,只需要一次BFS就可以搞定了。
练习:Hdu 1878 欧拉回路
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1878
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <queue> #include <map> #include <algorithm> #include <iostream> using namespace std; #define Maxn 1005 int n,m; int g[Maxn][Maxn]; int deg[Maxn]; int vis[Maxn]; bool progress() { for(int i=1;i<=n;i++) { if(deg[i]&1) return false; } return true; } bool bfs(int s) { queue<int> q; memset(vis,0,sizeof(vis)); q.push(s); vis[s] = 1; while(!q.empty()) { int temp = q.front(); q.pop(); for(int i=1;i<=n;i++) { if(g[temp][i] && !vis[i]) vis[i] = 1,q.push(i); } } for(int i=1;i<=n;i++) { if(vis[i] == 0) return false; } return true; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int a,b; while(scanf(" %d",&n)!=EOF) { if(n==0) break; memset(g,0,sizeof(g)); memset(deg,0,sizeof(deg)); scanf(" %d",&m); for(int i=0;i<m;i++) { scanf(" %d %d",&a,&b); g[a][b] = g[b][a] = 1; deg[a]++,deg[b]++; } if(!progress()) puts("0"); else if(bfs(1)) puts("1"); else puts("0"); } return 0; }
2.无向图的欧拉回路求法:
题目:Poj 1041 John's trip
题解:用并查集判断连通性(也可以用搜索)。然后用DFS求欧拉路径即可。欧拉回路有一个这样的性质,我们从图G中去掉一个圈得到新图G‘有欧拉回路,那么G也有欧拉回路,基于这个性质,一旦找到一个圈就消去,从图中拿出,直到图为空。另一种算法Fleury可以放下吧。。。
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <vector> #include <algorithm> using namespace std; #define Maxn 100 #define Maxm (2000<<2) vector <pair<int,int > > g[Maxn]; int vis[Maxm]; int father[Maxn]; int deg[Maxn]; int path[Maxm]; int top = 0; void init() { for(int i=0;i<Maxn;i++) { g[i].clear(); father[i] = i; } memset(deg,0,sizeof(deg)); } void addEdge(int a,int b,int z) { g[a].push_back(make_pair(b,z)); g[b].push_back(make_pair(a,z)); } int findset(int i) { return i == father[i] ? i : father[i] = findset(father[i]); } void merge(int a,int b) { a = findset(a),b = findset(b); if(a!=b) { father[a] = b; } } bool cmp(pair<int,int> a,pair<int,int> b) { return a.second < b.second; } bool judge() { //是否连通 for(int i=0;i<Maxn;i++) { sort(g[i].begin(),g[i].end(),cmp); for(int j=0;j<g[i].size();j++) { if(findset(i) != findset(g[i][j].first)) { return false; } } } //判断每个点的度是否是偶数 for(int i=0;i<Maxn;i++) { if(deg[i]!=0 && deg[i]&1 == 1) return false; } return true; } void euler(int s) { for(int i=0;i<g[s].size();i++) { if(!vis[g[s][i].second]) { vis[g[s][i].second] = 1; euler(g[s][i].first); //路径节点的位置在dfs之后 path[top++] = g[s][i].second; } } } void output() { for(int i=top-1;i>=0;i--) { if(i == top-1) printf("%d",path[i]); else printf(" %d",path[i]); } puts(""); } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int a,b,z; int s; while(scanf(" %d %d",&a,&b)!=EOF) { if(a == 0 && b==0) break; init(); s = min(a,b); scanf(" %d",&z); addEdge(a,b,z); merge(a,b); deg[a]++,deg[b]++; while(scanf(" %d %d",&a,&b)!=EOF) { if(a == 0 && b==0) break; scanf(" %d",&z); addEdge(a,b,z); merge(a,b); deg[a]++,deg[b]++; } if(judge()) { memset(vis,0,sizeof(vis)); top = 0; euler(s); output(); } else printf("Round trip does not exist.\n"); } return 0; }
练习无向图欧拉回路的建模:
Uva 10054 The Necklace
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=995
题解:每个颜色当作一个节点,每个珠子当作一条边。然后求此无向图的欧拉回路。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <vector> #include <algorithm> using namespace std; #define Maxn 60 #define Maxm 2000 struct Edge { int u,v; int id; }; vector<Edge> g[Maxn]; int edge_num,top; int father[Maxn],deg[Maxn],used[Maxn]; Edge path[Maxm]; bool vis[Maxm]; void init() { for(int i=0;i<Maxn;i++) { g[i].clear(); father[i] = i; deg[i] = 0,used[i] = 0; } memset(vis,false,sizeof(vis)); edge_num = 0; top = 0; } int findset(int i) { return i==father[i] ? i : father[i] = findset(father[i]); } void merge(int a,int b) { a = findset(a),b = findset(b); if(a!=b) father[a] = b; } void addEdge(int a,int b) { Edge tmp; tmp.u = a,tmp.v = b,vis[edge_num] = false,tmp.id = edge_num++; g[a].push_back(tmp); tmp.u = b,tmp.v = a,vis[edge_num] = false,tmp.id = edge_num++; g[b].push_back(tmp); } bool judge() { //degree can not be odd for(int i=0;i<Maxn;i++) { if(deg[i]&1) return false; } //must be connected int cnt = 0; for(int i=0;i<Maxn;i++) { if(used[i] && i == father[i]) cnt++; } return cnt == 1; } void output() { for(int i=top-1;i>=0;i--) { printf("%d %d\n",path[i].u,path[i].v); } } void euler(int s) { for(int i=0;i<g[s].size();i++) { if(!vis[g[s][i].id]) { vis[g[s][i].id] = vis[(g[s][i].id)^1] = 1; euler(g[s][i].v); path[top++] = g[s][i]; } } } int s; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int t; int n; int a,b,cas; cas = 0; scanf(" %d",&t); while(t--) { cas++; if(cas!=1) puts(""); printf("Case #%d\n",cas); init(); scanf(" %d",&n); for(int i=0;i<n;i++) { scanf(" %d %d",&a,&b); addEdge(a,b); merge(a,b); deg[a]++,deg[b]++; used[a] = 1,used[b] = 1; s = a; } if(judge()) euler(s),output(); else puts("some beads may be lost"); } return 0; }
如果一个有向图具有欧拉回路,充要条件是这个图是连通图,并且每个点的入度等于出度。
如果一个有向图具有欧拉路径(此图成为半欧拉图),充要条件是这个图是连通图,并且有一个点的入度-出度=1,另一个点的出度-入度=1,其余点的入度等于出度。
那么,如何判断一个有向图是否是连通图呢?
首先,我们需要明确一个概念:什么是连通图。
其实我们说有向图是连通图,指的是有向图是弱连通图。
有向图的连通性是一个大概念,包括强连通(任意两点都相互可达)、单向连通(任意两点至少可以有其中一点到达另一点)、弱连通(将有向图转换为无向图后是连通的)。
其中,强连通和弱连通都十分好判定(前者用Tarjan,后者直接用BFS即可)。
我们稍微来练习一下单向连通的判定:
题目:Poj 2762 Going from u to v or from v to u?
题目链接:http://poj.org/problem?id=2762
题意是判断一个有向图图是否能任取两点u,v都能够使得从u能够到达v,或者使得从v能够到达u。那么其实本题就是判断单连通性。
针对本题而言,首先我们使用Tarjan算法求出强连通分量,然后在缩点求出对应的Dag图,然后我们对此Dag进行拓扑排序,确定此Dag是否具有唯一的拓扑排序,如果不是,说明此图不存在欧拉回路。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <queue> #include <map> #include <stack> #include <algorithm> #include <iostream> using namespace std; #define Maxn 1005 #define Maxm 10005 int first[Maxn],next[Maxm]; int tot,tot2; struct Edge { int a,b; }edge[Maxm],edge2[Maxm]; //scc相关 int dfn[Maxn],low[Maxn],scccno[Maxn],dfs_clock,scc_cnt; bool instack[Maxn]; stack<int> st; //缩点相关 int first2[Maxn],next2[Maxm],indeg[Maxn]; void init() { memset(first,-1,sizeof(first)); tot = 0; } void addEdge(int a,int b) { edge[tot].a = a,edge[tot].b = b; next[tot] = first[a]; first[a] = tot++; } void addEdge2(int a,int b) { edge2[tot2].a = a,edge2[tot2].b = b; next2[tot2] = first2[a]; first2[a] = tot2++; } void tarjan(int u) { dfn[u] = low[u] = ++dfs_clock; st.push(u); instack[u] = true; for(int i=first[u];i!=-1;i=next[i]) { int v = edge[i].b; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(instack[v]) { low[u] = min(low[u],dfn[v]); } } if(low[u] == dfn[u]) { scc_cnt++; while(1) { int v = st.top(); st.pop(); instack[v] = false; scccno[v] = scc_cnt; if(u == v) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(instack,false,sizeof(instack)); for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i); } } void getDag(int n) { memset(first2,-1,sizeof(first2)); memset(indeg,0,sizeof(indeg)); tot2 = 0; for(int i=1;i<=n;i++) { for(int v=first[i];v!=-1;v=next[v]) { int j = edge[v].b; if(scccno[i]!=scccno[j]) { addEdge2(scccno[i],scccno[j]); indeg[scccno[j]]++; } } } } bool toposort(int n) { queue<int> q; for(int i=1;i<=n;i++) { if(indeg[i] == 0) q.push(i); } while(!q.empty()) { int temp = q.front(); q.pop(); if(!q.empty()) return false; for(int i=first2[temp];i!=-1;i=next2[i]) { int v = edge2[i].b; indeg[v]--; if(indeg[v] == 0) q.push(v); } } return true; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int n,m; int t; int a,b; scanf(" %d",&t); while(t--) { init(); scanf(" %d %d",&n,&m); for(int i=0;i<m;i++) { scanf(" %d %d",&a,&b); addEdge(a,b); } find_scc(n); getDag(n); if(toposort(scc_cnt)) puts("Yes"); else puts("No"); } return 0; }
4.有向图的欧拉回路求法:
其实做法是和无向图类似的,直接用DFS 即可解决。
判断入度和出度的关系,以及连通性(并查集可以判断弱连通性)
下面一道题练习欧拉路径:
Poj 2337 Catenyms
题目连接:http://poj.org/problem?id=2337
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <vector> #include <algorithm> using namespace std; #define Maxn 30 #define Maxm 2000 struct Edge { int vis,v; char str[30]; bool operator < (const Edge a) const { return strcmp(str,a.str)<0; } }; vector<Edge> g[Maxn]; int in[Maxn],out[Maxn]; int father[Maxn]; int used[Maxn]; int s; Edge path[Maxm]; int top; void init() { for(int i=0;i<Maxn;i++) { g[i].clear(); in[i] = 0; out[i] = 0; father[i] = i; } top = 0; memset(used,0,sizeof(used)); } int findset(int i) { return i == father[i] ? i : father[i] = findset(father[i]); } void merge(int a,int b) { a = findset(a); b = findset(b); if(a != b) father[a] = b; } //judge degree bool judge1() { int outNum = 0,inNum = 0; for(int i=0;i<Maxn;i++) { if(in[i] == out[i]) continue; else if(in[i] - out[i] == 1) inNum++; else if(out[i] - in[i] == 1) outNum++,s = i; else return false; } if(inNum == 0 && outNum == 0) return true; if(inNum ==1 && outNum == 1) return true; return false; } //judge connected bool judge2() { int cnt = 0; for(int i=0;i<Maxn;i++) { if(used[i] && i == father[i]) cnt++; } return cnt == 1; } void euler(int s) { for(int j=0;j<g[s].size();j++) { if(!g[s][j].vis) { g[s][j].vis = 1; euler(g[s][j].v); path[top++] = g[s][j]; } } } void output() { for(int i=top-1;i>=0;i--) { if(i == top-1) printf("%s",path[i].str); else printf(".%s",path[i].str); } puts(""); } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int t; int n; int x,y; char str[30]; scanf(" %d",&t); while(t--) { init(); scanf(" %d",&n); s = 100000; for(int i=0;i<n;i++) { scanf(" %s",str); x = str[0] - 'a'; y = str[strlen(str)-1] - 'a'; Edge tmp; strcpy(tmp.str,str),tmp.vis = 0,tmp.v = y; g[x].push_back(tmp); out[x]++,in[y]++; used[x] = used[y] = 1; merge(x,y); s = min(s,min(x,y)); } if(judge1() && judge2()) { for(int i=0;i<Maxn;i++) sort(g[i].begin(),g[i].end()); euler(s); output(); } else puts("***"); } return 0; }