题意:给出n个单词,一个单词的末尾字母和另一个单词的第一个字母相同,那么这两个单词就能连在一起,有的单词可以翻转,现在问能否把所有单词都连在一起并且每个单词使用一次且仅一次。
思路:把26个字母看成顶点,把每个单词看成一条边,如此建图,那么这个题就变成了判断这个图是否存在欧拉通路。我们知道欧拉通路的判断方法,但这题依然存在这问题,那就是单词可以翻转,也就是说,这个图中不仅存在有向边,而且还有无向边。这就变成了一个混合欧拉路径问题,可以通过网络流来解决这个问题。首先无向边任意选择一个方向,按有向边建图,然后统计每个顶点的入度和出度,入度和出度只差为奇数的点只能有0个或两个,否则无解。对于入度和出度只差为偶数的点,如果他们之间有无向边,那么把这条边反向,这个差就会缩小,问题就变成要把一些边反向,令入度和出度只差为0。把所有无向边按最开始规定的方向建图,每条边的容量为1,建立一个源点S和汇点T,对于出度>入度的顶点添加弧(S,u),容量为入度和出度的差值的一半(因为改一条边的方向这个差值会减少2),反之,添加(u,T)的弧。然后做最大流,判断是否满流。另外,还需要用并查集判断一下图的连通性。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
struct Edge
{
int from,to,cap,flow,next;
Edge(){};
Edge(int ff,int tt,int cc,int fl,int nx){from=ff;to=tt;cap=cc;flow=fl;next=nx;};
}edges[maxn<<2];
int ind[30],outd[30],parents[30];
int head[30],d[maxn],cur[maxn],nEdge;
bool vis[maxn];
void Init()
{
memset(ind,0,sizeof(ind));
memset(outd,0,sizeof(outd));
memset(head,0xff,sizeof(head));
for(int i=0;i<30;++i) parents[i]=i;
nEdge=-1;
}
void AddEdges(int from,int to,int cap)
{
edges[++nEdge]=Edge(from,to,cap,0,head[from]);
head[from]=nEdge;
edges[++nEdge]=Edge(to,from,0,0,head[to]);
head[to]=nEdge;
}
int Find(int x)
{
return x==parents[x]?x:parents[x]=Find(parents[x]);
}
bool BFS(int S,int T)
{
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int>q;
vis[S]=true;
q.push(S);
while(!q.empty())
{
int u=q.front();q.pop();
for(int k=head[u];k!=-1;k=edges[k].next)
{
Edge e=edges[k];
if(!vis[e.to]&&e.cap>e.flow)
{
d[e.to]=d[u]+1;
vis[e.to]=true;
q.push(e.to);
}
}
}
return vis[T];
}
int DFS(int u,int a,int T)
{
if(u==T||a==0) return a;
int flow=0,f;
for(int & k=cur[u];k!=-1;k=edges[k].next)
{
Edge e=edges[k];
if(d[e.to]==d[u]+1&&(f=DFS(e.to,min(a,e.cap-e.flow),T))>0)
{
edges[k].flow+=f;
edges[k^1].flow-=f;
flow+=f;a-=f;
if(a==0) break;
}
}
return flow;
}
int Maxflow(int S,int T)
{
int flow=0;
while(BFS(S,T))
{
for(int i=0;i<=T;++i) cur[i]=head[i];
flow+=DFS(S,inf,T);
}
return flow;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,tcase=0,n,type;
char str[30];
scanf("%d",&t);
while(t--)
{
tcase++;
Init();
scanf("%d",&n);
int cnt=0,u,v,len;
bool flag=true;
for(int i=0;i<n;++i)
{
scanf("%s%d",str,&type);
len=strlen(str);
u=(str[0]-'a'+1);
v=(str[len-1]-'a'+1);
if(type) AddEdges(u,v,1);
ind[v]++;outd[u]++;
if(Find(u)!=Find(v))
{
parents[Find(v)]=Find(u);
cnt++;
}
}
int S=0,T=27,odd=0,tmp,sum=0;
for(int i=1;i<=26;++i)
{
if(ind[i]||outd[i])
{
cnt--;
tmp=ind[i]-outd[i];
if(tmp%2) odd++;
else
{
if(tmp>0) {AddEdges(i,T,tmp/2);sum+=(tmp/2);}
else AddEdges(S,i,(-tmp)/2);
}
}
}
if(cnt!=-1||(odd!=0&&odd!=2)) flag=false;
tmp=Maxflow(S,T);
printf("Case %d: ",tcase);
if(tmp==sum&&flag)
printf("Well done!\n");
else
printf("Poor boy!\n");
}
return 0;
}