题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1116
题目意思:给出一些单词,玩单词接龙,如果能用完所有单词,则输出Ordering is possible.,否则输出The door cannot be opened.
算法分析:其实每个单词有用的部分只有首尾字母,每个单词相当于首字母指向未字母的一条有向边,一种可能是刚好是欧拉回路,另外一种可能就是刚好有一条这样的路径覆盖了所有单词。
光判断所有单词是否能组成欧拉回路或者路径是不够的,因为并不能保证只组成了一个欧拉回路或路径。所以,首先我们要检测所有单词参与构造路径是否在一个集合中。故此时用并查集实现,如果最后的集合总数大于1,那么这些单词比如不可能组成一条路。
在确定只有一个集合之后,我们只需判断,这条路是欧拉回路还是只是一条路径还是其他情况,如果是欧拉回路,那么所有点的入度等于出度,如果是一条路径,那么除起点和终点,其它点的入度也一定等于出度。其它情况都不可能用完所有单词。
下面是AC代码:
#include<iostream>
using namespace std;
const int maxn=205;
int fa[maxn],in[maxn],out[maxn],vis[maxn],p[maxn];
int Find(int x)
{
if(fa[x]==x) return x;
return fa[x]=Find(fa[x]);
}
void Merge(int x,int y)
{
x=Find(x);
y=Find(y);
if(x!=y) fa[y]=x;
}
int main()
{
int t,n;
string s;
cin>>t;
while(t--)
{
cin>>n;
for(int i=0;i<maxn;i++) vis[i]=0,in[i]=0,out[i]=0,fa[i]=i;
while(n--)
{
cin>>s;
char l=s[0],r=s[s.size()-1];
Merge(l,r);
in[r]++;
out[l]++;
vis[l]=1;
vis[r]=1;
}
int cnt=0;
for(int i=0;i<maxn;i++)
if(fa[i]==i&&vis[i]) cnt++;
if(cnt>1)//连通分支大于1,则不可能有欧拉回路
{
cout<<"The door cannot be opened."<<endl;
continue;
}
int j=0;
for(int i=0;i<maxn;i++)
if(in[i]!=out[i]) p[j++]=i;//保存入度和出度不相等的点
if(j==0)//如果不存在这样的点,那么必然是环路
{
cout<<"Ordering is possible."<<endl;
continue;
}
else if(j==2)//如果刚好出现了两个这样的点,那么再判断这两个点是否满足起点和终点情况
{
if((out[p[0]]-in[p[0]]==1&&in[p[1]]-out[p[1]]==1)||
(out[p[0]]-in[p[0]]==-1&&in[p[1]]-out[p[1]]==-1))
{
cout<<"Ordering is possible."<<endl;
continue;
}
}
cout<<"The door cannot be opened."<<endl;//其他情况
}
return 0;
}