一.原题链接:http://poj.org/problem?id=1386
二.题目大意:给出若干个单词,问能否通过排序,让前一个的最后一个字母等于下一个单词的第一个字母。
三.思路:将26个字母作为顶点,然后每个单词出现,就让第一个字母指向最后一个字母,其实只要记录出度入度就行了,然后就是有向图的欧拉通路判定:
1.图连通
2.所有点的出入度相等 等价于 有欧拉回路
3.存在2个出入度之差为1的点,那么出度大于入度的点是起点,另一个是终点。
四.代码:
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; const int MAX_SIZE = 27; int pre[MAX_SIZE], in[MAX_SIZE], out[MAX_SIZE]; void UFset() { memset(pre, -1, sizeof(pre)); } int Find(int x) { int root = x, save; while(pre[root] >= 0) root = pre[root]; for(save = pre[x]; x != root; pre[x] = root, x = save, save = pre[x]); return root; } void Union(int x, int y) { int Rootx = Find(x), Rooty = Find(y); int temp = pre[Rootx] + pre[Rooty]; if(pre[Rootx] > pre[Rooty]){ pre[Rootx] = Rooty; pre[Rooty] = temp; } else{ pre[Rooty] = Rootx; pre[Rootx] = temp; } } bool isConnected() { int i, first = -1; for(i = 0; i < 26; i++){ if(in[i] > 0 || out[i] > 0){ if(-1 == first) first = i; else if(Find(first) != Find(i)) return false; } } return true; } bool able() { int i, cntIn = 0, cntOut = 0; for(i = 0; i < 26; i++) if(in[i] != out[i]) if(1 == in[i] - out[i]){ cntIn++; if(cntIn > 1) return false; } else if(1 == out[i] - in[i]){ cntOut++; if(cntOut > 1) return false; } else return false; if(0 == cntIn && 0 == cntOut || 1 == cntIn && 1 == cntOut) return true; return false; } int main() { //freopen("in.txt", "r", stdin); int i, j, test, num, u, v; char buffer[1009]; scanf("%d", &test); while(test--){ memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); UFset(); scanf("%d", &num); for(i = 0; i < num; i++){ scanf("%s", buffer); u = buffer[0] - 'a'; v = buffer[strlen(buffer) - 1] - 'a'; if(u != v && Find(u) != Find(v)) Union(u, v); in[u]++, out[v]++; } if(isConnected() && able()){ printf("Ordering is possible.\n"); } else printf("The door cannot be opened.\n"); } return 0; }