题目:http://acm.hdu.edu.cn/showproblem.php?pid=1116(并查集+欧拉通路的判断,模板!!!)
思路:这个题目用到了并查集的操作,先将所有的点弄到一个或者多个集合中。并查集处理的过程中,将被处理的点标记一下,表示已经被访问过。过程中要统计各个点的出度和入度。
一般判断欧拉回(通)路的过程:
1)这个图必须是连通的,即根结点只有一个。如果不是,直接结束本次算法。
2)如果这个图是连通的,判断每个结点的入度和出度情况。
如果这个图是欧拉路,则每个顶点的出度等于入度。即out[i] = in[i]
如果这个图是半欧拉图,则起点的出度比入度大1,终点的入度比出度大1.其余顶点的出度等于入度。
如果满足上述条件,就可以将所有单词链接起来,否则不能。
当然,在判断出度入度的时候还有一点需要注意,那就是除了起点终点以外的顶点,出度必须等于入度(出度入度可以同时为2,即环),但是起点和终点必须保证出度和入度之差为1。
代码:
#include<iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=30;
int pre[maxn],in[maxn],out[maxn];//in out 分别为出度和入度
bool visit[maxn];//用来标记点是否被访问到
void buildset()//初始化
{
for(int i=0;i<maxn;i++){
pre[i]=i;
in[i]=out[i]=0;
visit[i]=false;
}
}
int find(int x)
{
int p,temp;
p=x;
while(x!=pre[x]) x=pre[x];
while(p!=x){
temp=pre[p];
pre[p]=x;
p=temp;
}
return x;
}
void join(int x,int y)
{
int p=pre[x];
int q=pre[y];
if(p!=q) pre[q]=p;
}
int main()
{
int T;scanf("%d",&T);while(T--){
int start,end;//作为一个字母的开头与结尾
buildset();
char str[1010];//带输入的字符串
int wordnum,len;//单词的数量以及某个单词的长度
int innum,outnum;//判断出度和入度不相等点的个数
int root;//根节点的个数
innum=outnum=root=0;
bool flag=true,flag1=true;//用来盘肚腩连通性
scanf("%d",&wordnum);
for(int i=1;i<=wordnum;i++){
scanf("%s",str);
len=strlen(str);
start=str[0]-'a'+1;
end=str[len-1]-'a'+1;
visit[start]=true,visit[end]=true;
out[start]++;
in[end]++;
join(start,end);
}
for(int i=1;i<maxn;i++){
if(visit[i]){//如果这个点被访问到,根据题目要求肯定只便利被访问到的点
if(pre[i]==i) root++;
if(out[i]!=in[i]){
if(in[i]-out[i]==1)
innum++;
else if(out[i]-in[i]==1)
outnum++;
else
flag1=false;//flag1 用来判断出度与入度之差,不相等的情况下只可能差1
}
if(root>1){
flag=false;//flag用来判断是否只有一个根节点
break;
}
}
}
if((flag && innum == 0 && outnum == 0 && flag1) || (flag && innum == 1 && outnum == 1 && flag1))//出度与入度和一样,以及出度入度差一的情况
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
}