大意:求混合图是否存在欧拉路径。
思路:欧拉路径?什么情况下存在欧拉路径?
(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;
}