题目
输入n(n≤100000)个单词,是否可以把所有这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同(例如 acm,malform,mouse)。每个单词最多包含1000个小写字母。输入中可以有重复单词。
解题思路
把字母看作结点,单词看作有向边,则问题有解等价于图中存在欧拉道路。有向图中存在欧拉道路的条件有两个:一是底图(忽略边的方向后得到的无向图)连通,二是度数满足不存在奇点或奇点数为2。度数判读只要在输入时记录每个顶点的入度出度,而连通性判断有两种:DFS和并查集。
代码实现
dfs判断连通性+特判入出度
1 #include<stdio.h> 2 #include<cstring> 3 using namespace std; 4 5 const int maxn = 26 + 5; 6 int G[maxn][maxn],in[maxn],out[maxn]; 7 int vis[maxn]; //点是否访问,不是边 8 int n; 9 char word[1000 + 10]; 10 11 void dfs(int u) 12 { 13 vis[u] = 1; 14 for (int v = 0; v < maxn; v++) if (G[u][v]) 15 { 16 G[u][v] = G[v][u] = 0; 17 //G[u][v]--; G[v][u]--; 18 dfs(v); 19 } 20 } 21 22 int main() 23 { 24 int T; 25 scanf("%d", &T); 26 while (T--) 27 { 28 memset(G, 0, sizeof(G)); 29 memset(in, 0, sizeof(in)); 30 memset(out, 0, sizeof(out)); 31 memset(vis, 0, sizeof(vis)); 32 scanf("%d", &n); 33 int start; //起点 34 while (n--) 35 { 36 scanf("%s", word); 37 int len = strlen(word); 38 int u = word[0] - 'a', v = word[len - 1] - 'a'; 39 start = u; 40 vis[u] = vis[v] = -1; //出现过的标为-1 41 G[u][v] = G[v][u] = 1; //连通性按无向图处理 42 //G[u][v]++; G[v][u]++; 43 out[u]++; //度数按有向图处理 44 in[v]++; 45 } 46 47 bool flag = true; //满足要求为true 48 int s_odd = 0,t_odd = 0; //起始奇点、结束奇点 49 for (int i = 0; i < maxn; i++) 50 { 51 if (in[i] == out[i]) continue; 52 if (out[i] == in[i] + 1 && !s_odd) { start = i; s_odd = 1; } 53 else if (in[i] == out[i] + 1 && !t_odd) t_odd = 1; 54 else { flag = false; break; } 55 } 56 if (flag) 57 { 58 dfs(start); //也可以不从奇点出发,这个只是判断连通性 59 for (int i = 0; i < maxn; i++) 60 if (vis[i] == -1) { flag = false; break; } 61 } 62 63 if (flag) printf("Ordering is possible. "); 64 else printf("The door cannot be opened. "); 65 } 66 return 0; 67 }
并查集判断连通性+特判入出度
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 using namespace std; 8 9 const int maxn = 26 + 5; 10 int in[maxn], out[maxn], flag[maxn], p[maxn], fa[maxn]; 11 int n; 12 13 void init() 14 { 15 for (int i = 0; i < 26; i++) 16 fa[i] = i; 17 memset(in, 0, sizeof(in)); 18 memset(out, 0, sizeof(out)); 19 memset(flag, 0, sizeof(flag)); 20 memset(p, 0, sizeof(p)); 21 } 22 int find(int x) 23 { 24 if (fa[x] != x) return fa[x] = find(fa[x]); 25 return fa[x]; 26 } 27 28 void unite(int x, int y) 29 { 30 int rx = find(x); 31 int ry = find(y); 32 fa[rx] = ry; 33 } 34 35 int main() 36 { 37 int T; 38 int a, b; 39 string str; 40 scanf("%d", &T); 41 while (T--) 42 { 43 init(); 44 scanf("%d", &n); 45 for (int i = 0; i < n; i++) 46 { 47 cin >> str; 48 a = str[0] - 'a'; 49 b = str[str.size() - 1] - 'a'; 50 unite(a, b); 51 in[a]++; 52 out[b]++; 53 flag[a] = flag[b] = 1; 54 } 55 56 int cnt = 0; //记录连通分量 57 int root; 58 for (int i = 0; i < 26; i++) 59 { 60 if (flag[i]) 61 { 62 root = find(i); 63 break; 64 } 65 } 66 for (int i = 0; i < 26; i++) 67 { 68 if (flag[i]) 69 if (root != find(i)) cnt = 1; 70 } 71 72 if (cnt) { 73 printf("The door cannot be opened. "); 74 continue; 75 } 76 77 int k = 0; //p[i]记录度数不等的 78 for (int i = 0; i < 26; i++) 79 { 80 if (flag[i] && in[i] != out[i]) p[k++] = i; 81 } 82 if (k == 0) 83 { 84 printf("Ordering is possible. "); 85 continue; 86 } 87 if (k == 2 && (in[p[0]] - out[p[0]] == 1 && in[p[1]] - out[p[1]] == -1) || (in[p[0]] - out[p[0]] == -1 && in[p[1]] - out[p[1]] == 1)) 88 { 89 printf("Ordering is possible. "); 90 continue; 91 } 92 else 93 { 94 printf("The door cannot be opened. "); 95 } 96 } 97 return 0; 98 }
参考链接:https://blog.csdn.net/qq_29169749/article/details/51111377