不懂欧拉路径以及欧拉回路的可以看看这篇:
有 N 个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。 你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。 请你编写一个程序,判断是否能达到这一要求。 输入格式 第一行包含整数 T,表示共有 T 组测试数据。 每组数据第一行包含整数 N,表示盘子数量。 接下来 N 行,每行包含一个小写字母字符串,表示一个盘子上的单词。 一个单词可能出现多次。 输出格式 如果存在合法解,则输出”Ordering is possible.”,否则输出”The door cannot be opened.”。 数据范围 1≤N≤105, 单词长度均不超过1000 输入样例: 3 2 acm ibm 3 acm malform mouse 2 ok ok 输出样例: The door cannot be opened. Ordering is possible. The door cannot be opened.
将每个单词的首尾字母看做一条边如:
acm:从a 到 m 连一条边,表示a可以到m。
然后将所有边都加到并查集中判断是否连通。
有向图欧拉回路成立条件:
所有点入度 == 出度
出起点终点外的点入度 == 出度。
代码如下:
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 30; int n; int p[N]; bool st[N]; int din[N], dout[N]; int find(int x)//并查集模板 { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int main() { int T; cin >> T; while (T -- ) { char str[1010]; scanf("%d", &n); memset(st, 0, sizeof st);//多组测试数据初始化 memset(din, 0, sizeof din); memset(dout, 0, sizeof dout); for (int i = 0; i < 26; i ++ ) p[i] = i;//初始化并查集 for (int i = 0; i < n; i ++ ) { scanf("%s", str); int len = strlen(str); int a = str[0] - 'a', b = str[len - 1] - 'a'; st[a] = st[b] = true;//标记点a, b dout[a] ++ , din[b] ++ ;//对于一条从a走到b的边,a的出边 + 1, b的入边加1 p[find(a)] = find(b);//将a, b加到并查集中 } bool success = true; int start = 0, last = 0; for (int i = 0; i < 26; i ++ ) if (din[i] != dout[i])//说明入度不等于出度 { if (din[i] == dout[i] + 1) last ++ ;//入度比出度多1为终点 else if (din[i] + 1 == dout[i]) start ++ ;//起点 else//否则说明无解 { success = false; break; } } //入度和出度都不等于0,或起点和终点都不唯一。说明无解 if (success && !(start == 0 && last == 0 || start == 1 && last == 1)) success = false; int rsp = -1; for (int i = 0; i < 26; i ++ )//判断所有点是否连通 if (st[i])//若该点出现过 { if (rsp == -1) rsp = find(i); else if (rsp != find(i))//说明不连通则无解 { success = false; break; } } if (success) puts("Ordering is possible."); else puts("The door cannot be opened."); } return 0; }