题目大意:
给一些单词,如果某个单词的首字母和另一个单词的尾字母相同,则两个单词可以连接起来。
要求判断能否把全部单词连接起来。
思路:
欧拉通路。
先构图:每个字母为点。然后对每个单词,将单词的首字母和尾字母连一个单向边。
#include<iostream>
using namespace std;
const int N=100005;
char str[1005];
int n;
struct Edge
{
int v,next;
}edge[N];
int edgehead[30];
int k=1;
int in[30];
int out[30];
bool start[30];
int pre[30];
int num;
int find(int u)
{
while(pre[u]>0)
{
u=pre[u];
}
return u;
}
void Union(int a,int b)
{
if(pre[a]<pre[b])
{
pre[a]+=pre[b];
pre[b]=a;
}
else
{
pre[b]+=pre[a];
pre[a]=b;
}
}
bool IsEuler()
{
int ng=0,nl=0,ne=0;
for(int i=1;i<=26;i++)
{
if(start[i])
{
if(in[i]==out[i])
{
ne++;
}
else if(in[i]==out[i]+1)
{
ng++;
}
else if(in[i]==out[i]-1)
{
nl++;
}
}
}
if((ne+ng+nl==num&&nl==1&&ng==1)||(ne==num))
return true;
else
return false;
}
void addedge(int u,int v)
{
start[u]=start[v]=1;
edge[k].v=v;
edge[k].next=edgehead[u];
edgehead[u]=k++;
in[v]++;
out[u]++;
}
bool Check()
{
int first=1;
int fat;
while(!start[first])
{
first++;
}
fat=find(first);
for(int i=first+1;i<=26;i++)
{
if(start[i]&&find(i)!=fat)
{
return false;
}
}
return true;
}
int main()
{
int cases;
scanf("%d",&cases);
while(cases--)
{
k=1;
memset(pre,-1,sizeof(pre));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(start,0,sizeof(start));
memset(edge,0,sizeof(edge));
memset(edgehead,0,sizeof(edgehead));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",str);
int a=str[0]-'a'+1;
int b=str[strlen(str)-1]-'a'+1;
addedge(a,b);
if(find(a)!=find(b))
Union(find(a),find(b));
}
num=0;
for(int i=1;i<=26;i++)
{
if(start[i])
num++;
}
if(!Check())
{
printf("The door cannot be opened.\n");
continue;
}
if(IsEuler())
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
return 0;
}