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;
}