参考:http://blog.csdn.net/lyy289065406/article/details/6647445 http://www.slyar.com/blog/poj-2513-c.html(谢谢他们)
这题主要是会把问题给转换为求Euler回路,即一笔画成并且不重复,反正我是想不到这点。把所有的端点当成顶点,木棍当成回路。这样就可以用判断是否能构成Euler回路来判断是否能连成一条直线。而判断是否能构成欧拉回路主要是根据它的两条性质:1.图是连通的;2.奇数节点的个数为0或者2。 对于判断图是否是连通的,我们可以用并查集来判断,如果是连通的,那么所有节点的fahter节点都应该是同一个节点。 然后再把每个节点的度数记录下来,就可以判断是否存在欧拉回路了。 由于输入的是字符,而且字符的长短还不一样,不好记录颜色的编号,于是我们想到可以用Trie树来判断每个颜色的编号。就这样,这题就解决了。所涉及的知识比较多,但都不是很难的,关键还是要会运用。
#include <iostream> #include <fstream> using namespace std; const int sunnum=27,base='a'; #define MAX 500005 class Trie{ public: bool terminal; //标记单词的结束 int id; //颜色的id Trie *sun[sunnum]; Trie() { terminal=false; id=0; memset(sun,0,sizeof(sun)); } }root; int color=0; //总的颜色个数 int degree[MAX]={0}; //第id个节点的总度数 int father[MAX]; //第id个节点的祖先 int rank[MAX]; //表示第id个节点的秩 void make_set(int x) { for(int i=1; i<=x; i++) { father[i]=i; } } //寻找祖先节点 int find_father(int x) { if(x!=father[x]) father[x]=find_father(father[x]); return father[x]; } //合并x->y void union_set(int x,int y) { int px=find_father(x); int py=find_father(y); father[px]=py; } //寻找颜色对应的值 int hash(char *s) { Trie *temp=&root; while(*s) { int k=*s-base; if(temp->sun[k]==0) temp->sun[k]=new Trie(); temp=temp->sun[k]; s++; } if(temp->terminal) //单词存在 { return temp->id; } else { temp->terminal=true; temp->id=++color; return temp->id; } } int main() { char a[11],b[11]; int i,j,odd_num=0,fa; //fa为公共祖先节点 make_set(MAX); freopen("acm.txt","r",stdin); while( scanf("%s %s",a,b)!=EOF ) { //a,b的颜色编号 i=hash(a); j=hash(b); degree[i]++; degree[j]++; union_set(i,j); } fa=find_father(1); //若连通,fa为所有节点的祖先节点 for(i=1; i<=color; i++) { if(degree[i]%2==1) //奇节点 odd_num++; if(find_father(i)!=fa) //非连通 { printf("Impossible\n"); return 0; } if(odd_num>2) { printf("Impossible\n"); return 0; } } if(odd_num==1) printf("Impossible\n"); else //奇节点是0或2 printf("Possible\n"); return 0; }