巧妙的模型转化
题目描述
来自 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