大意:求混合图是否存在欧拉路径。
思路:欧拉路径?什么情况下存在欧拉路径?
(1)、无向图中存在欧拉路径的条件:每个点的度数均为偶数或者有且仅有2个度数为奇数的点。
(2)、有向图中存在欧拉路径的条件:除了2个点外,其余的点入度=出度,且在这2个点中,一个点的入度比出度大1,另一个出度比入度大1。
(3)、由于我们只会混合图的欧拉回路,不会混合图的欧拉路径,于是我们将该问题转换为混合图的欧拉回路,怎么转换呢?找到出入度为奇数的两个点,一个起点,一个终点,从终点向起点连容量为1边的即可。
如果图不连通呢?对,先判是否连通,如果连通再判断图中出入度差dif的值为奇数的有几个,如果是0个,跳过,如果2个,执行(3),然后根据解一般混合图的欧拉回路的方法建图求解即可,这道题的关键在于“化归”思想的运用。
题目的大概思路也出来了,先判连通,可用并查集或者DFS,然后找出入度差为奇数的点,最后根据解一般混合图的欧拉回路算法求解即可。
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> using namespace std; const int MAXN = 101; const int MAXM = 20100; const int INF = 0x3f3f3f3f; struct Edge { int v, f; int next; }edge[MAXM]; int cnt; int n, m; int s, t; int totFlow; int tt; int first[MAXN], level[MAXN]; int ind[MAXN], outd[MAXN]; int q[MAXN]; int vis[MAXN]; int p[MAXN], rank[MAXN]; char str[MAXN]; void init() { cnt = 0; totFlow = 0; memset(first, -1, sizeof(first)); memset(ind, 0, sizeof(ind)); memset(outd, 0, sizeof(outd)); memset(vis, 0, sizeof(vis)); } void UFset() { for(int i = 1; i <= 26; i++) p[i] = i; memset(rank, 0, sizeof(rank)); } int find(int x) { return p[x] == x? x : p[x] = find(p[x]); } void Union(int x, int y) { x = find(x); y = find(y); if(x == y) return ; if(rank[x] > rank[y]) { p[y] = x; } else { p[x] = y; if(rank[x] == rank[y]) rank[y]++; } } void read_graph(int u, int v, int f) { edge[cnt].v = v, edge[cnt].f = f; edge[cnt].next = first[u], first[u] = cnt++; edge[cnt].v = u, edge[cnt].f = 0; edge[cnt].next = first[v], first[v] = cnt++; } int bfs(int s, int t) { memset(level, 0, sizeof(level)); level[s] = 1; int front = 0, rear = 1; q[front] = s; while(front < rear) { int x = q[front++]; if(x == t) return 1; for(int e = first[x]; e != -1; e = edge[e].next) { int v = edge[e].v, f = edge[e].f; if(!level[v] && f) { level[v] = level[x] + 1; q[rear++] = v; } } } return 0; } int dfs(int u, int maxf, int t) { if(u == t) return maxf; int ret = 0; for(int e = first[u]; e != -1; e = edge[e].next) { int v = edge[e].v, f = edge[e].f; if(level[v] == level[u] + 1 && f) { int Min = min(maxf-ret, f); f = dfs(v, Min, t); edge[e].f -= f; edge[e^1].f += f; ret += f; if(ret == maxf) return ret; } } return ret; } int Dinic(int s, int t) { int ans = 0; while(bfs(s, t)) ans += dfs(s, INF, t); return ans; } void read_case() { init(); UFset(); int n; scanf("%d", &n); while(n--) { int flag; scanf("%s %d", str, &flag); int u = str[0]-'a'+1, v = str[strlen(str)-1]-'a'+1; if(flag) read_graph(u, v, 1); outd[u]++, ind[v]++; Union(u, v); vis[u] = vis[v] = 1; } } int Check() //判连通性 { for(int i = 1; i <= 26; i++) { for(int j = i+1; j <= 26; j++) { if(vis[j] && vis[i]) { if(find(i) != find(j)) return 0; } } } return 1; } int build() { read_case(); s = 0, t = 27; if(!Check()) return 0; int tot = 0; int u = 0, v = 0; for(int i = 1; i <= 26; i++) if(vis[i]) //存在0个或者2个入度为奇数的点,说明存在欧拉路径。 { int dif = outd[i]-ind[i]; if(dif%2 == 1 || dif%2 == -1) { tot++; if(dif > 0) u = i; //起点 if(dif < 0) v = i; //终点 } } if(tot == 0 || (tot == 2 && u && v)) { if(tot == 2) read_graph(v, u, 1); //若有两个度数为奇数的点,假设存在欧拉路径,添加一条容量为1的边,构成欧拉回路,不影响结果。 } else return 0; for(int i = 1; i <= 26; i++) if(vis[i]) { int dif = outd[i]-ind[i]; if(dif > 0) { read_graph(s, i, dif/2); totFlow += dif/2; } else read_graph(i, t, -dif/2); } return 1; } void solve() { int flag = build(); int ans = Dinic(s, t); printf("Case %d: ", tt); if(!flag) printf("Poor boy!\n"); else if(ans >= totFlow) printf("Well done!\n"); else printf("Poor boy!\n"); } int main() { int T, times = 0; scanf("%d", &T); for(tt = 1; tt <= T; tt++) { solve(); } return 0; }