题目:
来自 ICPC CERC 1999/2000,有改动。
有 N N N个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。请你编写一个程序,判断是否能达到这一要求。如果能,请给出一个合适的顺序。
输入:
多组数据。第一行给出数据组数 T T T ,每组数据第一行给出盘子数量 N N N ,接下去 N N N 行给出小写字母字符串,一种字符串可能出现多次。
输出:
若存在一组合法解输出Ordering is possible.,否则输出The door cannot be opened.。
样例输入:
3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
样例输出:
The door cannot be opened.
Ordering is possible.
The door cannot be opened.
题解:
题意是将每个盘子连接起来,要满足相邻两个盘子的末尾和起点相同。
初始思路:将盘子看成点,如果找到两两盘子首末字母相同就建一条边,最后走一遍欧拉通路。但这显然是错的,因为欧拉通路要经过每一条边且不能重复,那麽只要两盘子建了边,它都要算,但题目是只要相邻两盘子满足条件即可,所以这种方法会跑重。
正解:
我们可以用每个盘子首末字母看成点,在每个盘子的首末字母间建一条边(即可以看成以每个盘子为边),最后对所有盘子的首末字母跑一遍欧拉通路即可,如果可以连通,即每条边(盘子)都可以连上,那么就满足题意,反之不满足。
如这一组:
3
acm
malform
mouse
有图:
它是欧拉通路所以满足。
最后还要判一遍这里面的点连不连通,所以可以用并查集,如果判到两个字母,那么就并到一起,最后判一遍即可
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 100005;
int t, n, ru[MAXN], tot, chu[MAXN], fa[30];
string h;
int Find_Set(int x) {
if(fa[x] == x) {
return x;
}
return fa[x] = Find_Set(fa[x]);
}
int main() {
scanf("%d", &t);
while (t--) {
memset(chu, 0, sizeof(chu));
memset(ru, 0, sizeof(ru));
scanf("%d", &n);
for(int i = 1;i <= 26; i++) fa[i] = i;
for (int i = 1;i <= n; i++) {
cin>>h;
int siz = h.size();
int x1 = h[0] - 'a' + 1, x2 = h[siz - 1] - 'a' + 1;
chu[x1]++;
ru[x2]++;
int u = Find_Set(x1), v = Find_Set(x2);
if(u != v) {
fa[v] = u;
}
}
int uu = 0;
for(int i = 1; i <= 26; i++) {
if((chu[i] || ru[i]) && fa[i] == i) {
uu++;
}
}
if(uu == 1) {
int ans = 0, ans1 = 0;
bool flag = 1;
for(int i = 1;i <= 26; i++) {
if(chu[i] != ru[i]) {
if(chu[i] - ru[i] == 1) {
ans++;
}
else if(ru[i] - chu[i] == 1) {
ans1++;
}
else{
flag = 0;
break;
}
}
}
if(flag && ans1 <= 1 && ans <= 1) {
printf("Ordering is possible.\n");
}
else{
printf("The door cannot be opened.\n");
}
}
else{
printf("The door cannot be opened.\n");
}
}
return 0;
}