#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1000 + 5;
// 并查集(代码摘自《算法竞赛入门经典——训练指南》第三章)
int pa[256];
int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; } //找到x的代表元
int used[256], deg[256]; // 是否出现过;度数
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n;
char word[maxn];
scanf("%d", &n);
memset(used, 0, sizeof(used));
memset(deg, 0, sizeof(deg));
for(int ch = 'a'; ch <= 'z'; ch++) pa[ch] = ch; // 初始化并查集
int cc = 26; // 连通块个数
for(int i = 0; i < n; i++) {
scanf("%s", word);
char c1 = word[0], c2 = word[strlen(word)-1];
deg[c1]++; //出度++
deg[c2]--; //入度--
used[c1] = used[c2] = 1; //该结点使用过
int s1 = findset(c1), s2 = findset(c2); //找到c1和c2的代表元
if(s1 != s2){ //c1和c2属于不同的连通分量
pa[s1] = s2; cc--; //c1及其所在的连通分量并入s2中,连通分量个数少1
}
}
vector<int> d;
for(int ch = 'a'; ch <= 'z'; ch++) {
if(!used[ch]) cc--; // 没出现过的字母
else if(deg[ch] != 0) d.push_back(deg[ch]); //保存出度入度不相等的结点的出入度差值
}
bool ok = false;
//是连通图,并且所有结点的出度和入度相等或只有两个结点出入度不相等且差值为1
if(cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) ok = true;
if(ok) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}
07-27
313
