题意:给出一些单词,问是否能将它们全部排成一列使上一个单词的尾字母和下一个单词的头字母相同
思路:已字母未点,一个单词看成一条有向图,求是否能得到欧拉道路(每条路经过一次,从起点到终点)或欧拉回路(每条路经过一次,从一点能回到这一点)
1)(有向图)欧拉道路:有一个点入度比出度大1,有一个点出度比入度大1,其余点出度等于入度
2)(有向图)欧拉回路:各点出度等于入度
3)在判断欧拉道路和欧拉回路之前要先判断各点是否连通(一般步骤为:1.判断连通性,2.看奇点,3.求路径)
#include
#include
int fa[30];
int find(int x)
{
int temp = x;
while(x != fa[x])
{
x = fa[x];
}
fa[temp] = x;
return x;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
int t, n, countin, countout, father, apart;
char s[1005];
int in[30], out[30];
scanf("%d", &t);
while(t--)
{
for(int i = 0; i < 29; i++)
{
in[i] = 0; out[i] = 0; fa[i] = 0;
}
scanf("%d", &n);
while(n--)
{
scanf(" %s", s);
int a = s[0] - 'a' + 1, b = s[strlen(s) - 1] - 'a' + 1;
in[b]++; out[a]++;
int x = find(a), y = find(b);
if(!x) {fa[a] = a; x = a;} //未出现过的字母(点),find=0
if(!y) {fa[b] = b; y = b;}
if(x != y) fa[x] = y; //两点未连通时将它们连通,放入同一集合
}
apart = 0;
for(int i = 1; i <= 26; i++)
{//求根节点数,即集合数
int x = find(i);
if(x && x == i) apart++;
}
if(apart > 1)
{//不联通
printf("The door cannot be opened.\n");
continue;
}
countin = 0; countout = 0;
for(int i = 1; i <=26; i++)
{//记录奇点的出度入度情况
if(in[i] > out[i]) countin += in[i] - out[i];
else countout += out[i] - in[i];
}
if(countin + countout == 0)
{
printf("Ordering is possible.\n");
}
else if(countin == 1 && countout == 1)
{
printf("Ordering is possible.\n");
}
else printf("The door cannot be opened.\n");
}
return 0;
}