题目链接:http://poj.org/problem?id=2513
题目大意:
有N根木棒,一根木棒有2头,我们把每头涂色(相同或不同),如果2根木棒有相同颜色的一端就可以连接,颜色全部不同就不能连接,现在给你N根木棒以及它们的颜色,问最后能不能链接成1条链。
解题思路:
本质上是一个欧拉路的问题,但是因为颜色是字符串,我们没法简单的处理(用map会超时~。~),所以应该通过一种方法把每个单词转换成一个数字,然后就变成一个典型的欧拉路问题了。
可以使用字典树处理颜色,对每个颜色返回1个值,然后用并查集判断连通图+欧拉路解决即可。
原来写的pre数组是pre[i] = i,优化后变成pre[i] = -1,这样,我们赋值时候可以使用memset,速度加快。然后Find_Set采用递归+路径压缩,insert返回数组下标,字典树采用静态的数组,速度加快。优化后速度由900+ms变为400+ms。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define CLR(arr, what) memset(arr, what, sizeof(arr)) const int N = 15; const int M = 1000010; struct Trie { int num; Trie* next[26]; }*Head; char u[N], v[N]; int num, n, top, total; bool flag; int pre[M], in[M]; Trie trie[M]; int Find_Set(int x) //递归+路径压缩 { return pre[x] == -1 ? x : (pre[x] = Find_Set(pre[x])); } void Make_Set(int x, int y) //无按秩合并 { int root1 = Find_Set(x); int root2 = Find_Set(y); if(root1 != root2) pre[root2] = root1; } int insert(char str[]) //返回数组下标 { Head = &trie[0]; int len = strlen(str); for(int i = 0; i < len; ++i) { int temp = str[i] - 'a'; if(Head->next[temp] == NULL) Head->next[temp] = &trie[++num]; Head = Head->next[temp]; } if(Head->num == 0) Head->num = top++; return Head->num; } void init() { num = total = 0; top = 1; flag = true; CLR(in, 0); CLR(pre, -1); for(int i = 0; i < 2 * n + 2; ++i) { trie[i].num = 0; for(int j = 0; j < 26; ++j) trie[i].next[j] = NULL; } } int main() { int tmp, temp; init(); while(scanf("%s %s", &u, &v) != EOF) { tmp = insert(u); temp = insert(v); in[tmp]++; in[temp]++; Make_Set(tmp, temp); } int root = Find_Set(1); for(int i = 1; i < top; ++i) //是否连通+奇度点个数 { if(root != Find_Set(i)) { flag = false; break; } if(in[i] & 1) total++; if(total > 2) break; } puts(((total == 0 || total == 2) && flag == true) ? "Possible" : "Impossible"); return 0; }