【思维题 欧拉图】loj#10106. 单词游戏

巧妙的模型转化

题目描述

来自 ICPC CERC 1999/2000,有改动。

有 NNN 个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。请你编写一个程序,判断是否能达到这一要求。如果能,请给出一个合适的顺序。

输入格式

多组数据。第一行给出数据组数 TTT,每组数据第一行给出盘子数量 NNN,接下去 NNN 行给出小写字母字符串,一种字符串可能出现多次。

输出格式

若存在一组合法解输出Ordering is possible.,否则输出The door cannot be opened.


题目分析

看到题最自然的想法当然是求哈密尔顿路。然而哈密尔顿路并没有什么好的性质,所以考虑另一种建模:欧拉图。

每一个单词有用的信息只有首尾两个字母,那么建一张图令节点表示小写字母;边表示一种单词的转移。问题就变成了在图中判断欧拉路径的存在性。

若有向图G存在欧拉路径(即为半欧拉图),那么当且仅当G的基图联通且存在顶点$u$的入度比出度大1,$v$的入度比出度小1,其他所有顶点的入度等于出度。

代码内有些处理细节的精妙之处。

 1 #include<bits/stdc++.h>
 2 
 3 int T,n,m;
 4 char s[1035];
 5 int fa[35],u[35],v[35],cnt,cnt1,cnt2;
 6 
 7 int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
 8 int abs(int a){return a>0?a:-a;}
 9 int main()
10 {
11     scanf("%d",&T);
12     while (T--)
13     {
14         memset(u, 0, sizeof u);
15         memset(v, 0, sizeof v);
16         scanf("%d",&n);
17         cnt = cnt1 = cnt2 = 0;
18         for (int i=1; i<=30; i++) fa[i] = i;
19         for (int i=1; i<=n; i++)
20         {
21             scanf("%s",s+1), m = strlen(s+1);
22             int l = s[1]-'a'+1, r = s[m]-'a'+1;
23             fa[get(l)] = get(r), u[l]++, v[r]++;
24         }
25         for (int i=1; i<=26; i++)
26             if (((u[i]||v[i])&&(get(i)==i))||abs(u[i]-v[i]) > 1) cnt++;
          //连通块个数的统计          //出入度不相等的点个数
27 if (cnt > 1){ 28 puts("The door cannot be opened."); 29 continue; 30 } 31 for (int i=1; i<=26; i++) 32 if (u[i] > v[i]) cnt1++; 33 else if (u[i] < v[i]) cnt2++; 34 if (cnt1!=cnt2||cnt1 > 1) 35 puts("The door cannot be opened."); 36 else puts("Ordering is possible."); 37 } 38 return 0; 39 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9780794.html