http://acm.hdu.edu.cn/showproblem.php?pid=1116
分析:本题虽然在并查集分类中,但是主要是对欧拉图、欧拉通路的判断
补充:
基本概念:
欧拉通路:图G(有向,无向)中经过每条边一次并且仅一次的通路。
欧拉回路:若G中含有欧拉通路又是回路,则称为欧拉回路。
欧拉图: 具有欧拉回路的图称为欧拉图。
定理、推论:
无向图:
图G具有欧拉通路充要条件:G是连通图且无奇度顶点或有两个奇度顶点;
若无两个奇度顶点,则通路为回路;
若有两个奇度顶点,则他们是每条欧拉通路的端点(每条,个人的理解是虽然起点终点相同,但是可能过程不同)。
无向图G为欧拉图,当且仅当G是连通的且无奇度顶点。
有向图:
图D具有欧拉通路充要条件:D是连通的,且除了两个例外的顶点(一个顶点出度比入度大一,另一个相反),其余顶点入度等于出度。
D是欧拉图的充要条件: D是连通的,且所有顶点的入度等于出度。
代码:
//hdu 1116 play on a word
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdio>
using namespace std;
#define MAX 30
int father[MAX],in[MAX],out[MAX];
bool vis[MAX];
int Find(int x)
{
return x==father[x] ? x:Find(father[x]);
}
void my_union(int x,int y)
{
int root1=Find(x);
int root2=Find(y);
if(root1!=root2) father[root2]=root1;
}
int main()
{
int t,n;
string str; int len;
int incnt,outcnt,root; //记录出度-入度=1的个数,根的数目
int lm, em; //开始字母与结束字母
int flag1,flag2;
scanf("%d",&t);
while(t--){
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(vis,false,sizeof(vis));
for(int i=1;i<MAX;i++) father[i]=i; //初始化不可少
flag1=1;flag2=1;
incnt=0;outcnt=0;root=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
cin>>str; len=str.length();
lm=str[0]-'a'+1;
em=str[len-1]-'a'+1;
out[lm]++; in[em]++;
vis[lm]=true; vis[em]=true;
my_union(lm,em);
}
for(int i=1;i<MAX;i++){
if(vis[i]){
if(father[i]==i) root++;
if(in[i]!=out[i]){
if(in[i]-out[i]==1) incnt++;
else if(out[i]-in[i]==1) outcnt++;
else flag2=false;
}
}
if(root>1){
flag1=false;
break;
}
}
if((flag1&&flag2&&(!incnt)&&(!outcnt))||
(flag1&&flag2&&(incnt==1)&&(outcnt==1)))
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
return 0;
}