题目描述:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19492
/*1:通常想法是将每个单词看成一个节点,但如果单词的尾部与另外一个单词的首部相同,就能架起一座“桥”,但这样以来,数组就开太大了 2:将每个单词的首尾两个字母看成节点,此时单词就是一座桥。这样就可以根据欧拉道路的出入度来判断 3:注意题中判断欧拉道路的起点以及终点的技巧*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
const int maxc = 1000 + 5;
int n, in[30], out[30], graph[30][30], start, visit[30]; //从start这个点开始dfs
void dfs(int u) {
visit[u] = 1;
for(int v = 0; v < 26; v++) {
if(graph[u][v] > 0) { //因为节点可以重复,所以不能在条件里加上 && !visit[v]
graph[u][v]--; graph[v][u]--;
dfs(v);
}
}
}
bool isConnected() {
memset(visit, 0, sizeof(visit));
dfs(start);
for(int i = 0; i < 26; i++)
if(in[i] + out[i]) //出入度为0的字母没有出现过,此时不计
if(!visit[i]) return false;
return true;
}
bool solve() {
if(!isConnected()) return false;
int theBegin = 0, theEnd = 0;
for(int u = 0; u < 26; u++) {
if(in[u] != out[u]) {
if(!theBegin && out[u] - in[u] == 1) theBegin = 1;
else if(!theEnd && in[u] - out[u] == 1) theEnd = 1;
else return false;
}
}
return true;
}
int main() {
int kase;
scanf("%d", &kase);
while(kase--) {
scanf("%d", &n);
//初始化
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(graph, 0, sizeof(graph));
//输入同时确定出度以及入度
string str;
for(int i = 0; i < n; i++) {
cin >> str;
int u = str[0] - 'a'; int v = str[str.length() - 1] - 'a';
out[u]++; in[v]++;
graph[u][v]++; graph[v][u]++;//注意虽然是有向图,但这行代码这样写的目地是为了dfs能够顺利遍历整张图,不然有些点访问不到
start = u;
}
if(solve()) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
}