POJ 2513 Colored Sticks(欧拉回路判断+字典树Trie+并查集)
http://poj.org/problem?id=2513
题意:
给你多个木棍,每个木棍两段涂上颜色,两根木棍只有在相同颜色的一端才能连接,问你能不能使所有木棍都连接成一条直线路.
分析:
将输入的每个颜色看出是图的一个点,然后每条木棍正好是连接了两种颜色的一条边,我们只需要判断这个图中是否存在欧拉道路或欧拉回路即可.(考虑一下这种情况,如果一根棍子首尾是同种颜色怎么办?其实这个可以不用考虑,这就是一个自环,如果不含自环的图有欧拉回路,那么含自环的图一定也有欧拉回路)
处理流程:依次读入每根木棍的两段的颜色A和B,然后尝试将A与B插入字典树,如果不存在A颜色,就新加入A颜色,并且给A颜色一个数字编号i,B颜色的编号是j.
i和j就是图中的两个节点且它们是连通的且它们的度数还要都+1.合并i与j的并查集(如果i与j是同种颜色,那么就不会合并它们).当处理完所有的木棍后,看看该图是不是连通的.在看看该图的所有节点的度数是不是满足下面要求:
所有点的度数都是偶数 或 只有2个点的度数是奇数.
AC代码:1A,438ms
#include <iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxnode=5000000+1000; const int sigma_size=26; int cnt;//记录当前已经读入了cnt个不同的颜色了 struct Trie { int ch[maxnode][sigma_size]; int val[maxnode];//单词节点的val才非0,且val[i]表示的是该单词节点的编号 int sz; void init() { sz=1; memset(ch[0],0,sizeof(ch[0])); val[0]=0; } int insert(char *s) { int n=strlen(s),u=0; for(int i=0;i<n;i++) { int id=s[i]-'a'; if(ch[u][id]==0) { ch[u][id]=sz; val[sz]=0; memset(ch[sz],0,sizeof(ch[sz])); sz++; } u=ch[u][id]; } if(val[u]==0)//新颜色 val[u]=++cnt; return val[u]; } }trie; const int MAXN=500000+1000; int F[MAXN];//初始为-1 int r[MAXN];//记录每个点的度数,初始为0 int findset(int x) { if(F[x]==-1) return x; return F[x]=findset(F[x]); } void bind(int i,int j) { int fa=findset(i); int fb=findset(j); if(fa!=fb) F[j]=i; } char str1[15],str2[15]; int main() { trie.init(); memset(r,0,sizeof(r)); cnt=0; memset(F,-1,sizeof(F)); while(scanf("%s%s",str1,str2)==2) { int i=trie.insert(str1);//尝试插入新颜色 int j=trie.insert(str2); r[i]++;//度数加1 r[j]++; int fa=findset(i),fb=findset(j); if(fa!=fb) bind(i,j); } bool connect=true,degree=false;//图是否连通,图的节点度数是否符合要求 for(int i=2;i<=cnt;i++) if(findset(i)!=findset(1)) { connect=false; break; } if(connect) { int odd=0; for(int i=1;i<=cnt;i++) if(r[i]%2==1) odd++; if(odd==2||odd==0) degree=true; } if(connect && degree) printf("Possible\n"); else printf("Impossible\n"); return 0; }