UVa 10129 (并查集 + 欧拉路径) Play on Words
题意:
有n个由小写字母的单词,要求判断是否存在某种排列使得相邻的两个单词,前一个单词末字母与后一个单词首字母相同。
分析:
将单词的两个字母看做节点,则一个单词可以看做一条有向边。那么题中所求的排列就等价于该有向图中是否存在欧拉路径。
在判断之前,首先要确定这个图是连通的,代码中用并查集来实现。
回顾一下存在欧拉路径的条件,全都是偶点或者有且仅有两个奇点。我们用deg来记录每个点的度,出度为1,入度为-1。
程序中判断存在欧拉路径的条件就是:deg全为0 或者 有两个不为0的,其中一个为1一个为-1
used记录某个字母是否出现过。
1 //#define LOCAL 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn = 1000 + 10; 8 char word[maxn]; 9 int pa[256], deg[256], cc, used[256]; 10 11 int find(int a) 12 { return pa[a] == a ? a : pa[a] = find(pa[a]); } 13 14 int main(void) 15 { 16 #ifdef LOCAL 17 freopen("10129in.txt", "r", stdin); 18 #endif 19 20 int T, n; 21 scanf("%d", &T); 22 while(T--) 23 { 24 memset(used, 0, sizeof(used)); 25 memset(deg, 0, sizeof(deg)); 26 for(int i = 'a'; i <= 'z'; ++i) 27 pa[i] = i; 28 cc = 26; //Á¬Í¨¿éµÄÊýÁ¿ 29 30 scanf("%d", &n); 31 for(int i = 0; i < n; ++i) 32 { 33 scanf("%s", word); 34 char c1 = word[0]; 35 char c2 = word[strlen(word) - 1]; 36 used[c1] = used[c2] = 1; 37 deg[c1]++; deg[c2]--; 38 int p1 = find(c1); 39 int p2 = find(c2); 40 if(p1 != p2) 41 { 42 cc--; 43 pa[p1] = p2; 44 } 45 } 46 47 vector<int> d; 48 for(int i = 'a'; i <= 'z'; ++i) 49 { 50 if(!used[i]) --cc; 51 else if(deg[i]) d.push_back(i); 52 } 53 bool ok = false; 54 if(cc == 1 && (d.empty() || (d.size() == 2 && (deg[d[0]] == 1 || deg[d[0]] == -1)))) ok = true; 55 if(ok) puts("Ordering is possible."); 56 else puts("The door cannot be opened."); 57 } 58 59 return 0; 60 }