题意:
给出一个图,有的边是有向边,有的是无向边。试找出一条欧拉回路。
分析:
按照往常的思维,遇到混合图,我们一般会把无向边拆成两条方向相反的有向边。
但是在这里却行不通了,因为拆成两条有向边的话,就表示这个边能“在两个相反方向各经过一次”。
而题意是这个边只能经过一次。
假设图中存在欧拉回路,则所有点的出度out(i) 等于 入度in(i)
不妨这样,先将所有的无向边任意定向,对于out(u) > in(u)的点,可以将已经定向的无向边u->v反向为v->u,这样out(u) - in(u)的值减2
如果把出度看做“货物”,则out(u) > in(u)的点提供货物,out(u) < in(u)的点需要货物,所以我们可以用网络流求最大流的算法,来使“供需平衡”
具体来说就是,给已经定向的无向边两点之间连一条容量为1的边,连接源点 与 所有“提供”出度的点,连接 所有“需要”出度的点 与 汇点。
如果求出来的最大流满载,也就是所有的出度都能被运到需要的地方,则有解。
在最大流中,如果流量为1则代表将该边反向的操作。
所以再建一个新图来求欧拉回路。
本题还有一个坑点就是可能存在平行边,所以求欧拉路径的过程中用 vis[u][v] = 1;的方法是行不通的了。
1 #include <bits/stdc++.h> 2 #define REP(i,n) for(int i = 0; i < (n); i++) 3 #define PB push_back 4 using namespace std; 5 6 const int INF = 1000000000; 7 const int maxn = 500 + 10; 8 9 struct Edge 10 { 11 int from, to, cap, flow; 12 Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {} 13 }; 14 15 struct EdmondsKarp 16 { 17 int n, m; 18 vector<Edge> edges; 19 vector<int> G[maxn]; 20 int a[maxn], p[maxn]; 21 22 void Init(int n) 23 { 24 REP(i, n) G[i].clear(); 25 edges.clear(); 26 } 27 28 void AddEdge(int from, int to, int cap) 29 { 30 edges.PB(Edge(from, to, cap, 0)); 31 edges.PB(Edge(to, from, 0, 0)); 32 m = edges.size(); 33 G[from].PB(m-2); 34 G[to].PB(m-1); 35 } 36 37 int MaxFlow(int s, int t) 38 { 39 int flow = 0; 40 for(;;) 41 { 42 queue<int> Q; 43 Q.push(s); 44 memset(a, 0, sizeof(a)); 45 a[s] = INF; 46 while(!Q.empty()) 47 { 48 int x = Q.front(); Q.pop(); 49 REP(i, G[x].size()) 50 { 51 Edge& e = edges[G[x][i]]; 52 if(!a[e.to] && e.cap > e.flow) 53 { 54 a[e.to] = min(a[x], e.cap - e.flow); 55 p[e.to] = G[x][i]; 56 Q.push(e.to); 57 } 58 } 59 if(a[t]) break; 60 } 61 if(!a[t]) break; 62 for(int u = t; u != s; u = edges[p[u]].from) 63 { 64 edges[p[u]].flow += a[t]; 65 edges[p[u]^1].flow -= a[t]; 66 } 67 flow += a[t]; 68 } 69 return flow; 70 } 71 }g; 72 73 int n, m; 74 int deg[maxn], u[maxn], v[maxn], id[maxn]; 75 bool directed[maxn]; 76 77 vector<int> G[maxn];//建新图,用来求欧拉回路 78 vector<int> vis[maxn]; 79 vector<int> path;//欧拉回路 80 81 void Euler(int u) 82 { 83 REP(i, G[u].size()) if(!vis[u][i]) 84 { 85 vis[u][i] = 1; 86 Euler(G[u][i]); 87 path.PB(G[u][i]+1); 88 } 89 } 90 91 void print_answer() 92 { 93 REP(i, n) { G[i].clear(); vis[i].clear(); } 94 REP(i, m) 95 { 96 bool rev = false; 97 if(!directed[i] && g.edges[id[i]].flow > 0) rev = true;//流量为1对应将该边反向 98 if(!rev) { G[u[i]].PB(v[i]); vis[u[i]].PB(0); } 99 else { G[v[i]].PB(u[i]); vis[v[i]].PB(0); } 100 } 101 102 path.clear(); 103 Euler(0); 104 printf("1"); 105 for(int i = path.size()-1; i >= 0; i--) printf(" %d", path[i]); 106 puts(""); 107 } 108 109 int main() 110 { 111 //freopen("in.txt", "r", stdin); 112 113 int T; 114 scanf("%d", &T); 115 while(T--) 116 { 117 scanf("%d%d", &n, &m); 118 g.Init(n+2); 119 memset(deg, 0, sizeof(deg)); 120 REP(i, m) 121 { 122 char d; 123 scanf("%d %d %c", &u[i], &v[i], &d); 124 u[i]--; v[i]--; 125 directed[i] = (d == 'D'); 126 deg[u[i]]++; deg[v[i]]--; 127 if(!directed[i]) { id[i] = g.edges.size(); g.AddEdge(u[i], v[i], 1); }//第i条边在网络流中的编号 128 } 129 130 bool ok = true; 131 REP(i, m) if(deg[i] % 2 != 0) { ok = false; break; }//出入度之和不是偶数说明不存在欧拉回路 132 133 int s = n, t = n+1; 134 if(ok) 135 { 136 int sum = 0; 137 REP(i, n) 138 { 139 if(deg[i] > 0) { sum += deg[i] / 2; g.AddEdge(s, i, deg[i] / 2); } 140 if(deg[i] < 0) { g.AddEdge(i, t, -deg[i] / 2); } 141 } 142 int flow = g.MaxFlow(s, t); 143 if(flow != sum) ok = false;//最大流不满载 144 } 145 146 if(ok) print_answer(); else puts("No euler circuit exist"); 147 if(T) puts(""); 148 } 149 150 return 0; 151 }