PKU 2513 Colored Sticks(并查集+Trie树+欧拉路径(回路))
题目大意:
给定一些木棒,木棒两端都涂上颜色,求是否能将木棒首尾相接,连成一条直线,要求不同木棒相连接的一端必须是同颜色的。
解题思路:
可以用图论中欧拉路的知识来解这道题,首先可以把木棒两端看成节点,把木棒看成边,这样相同的颜色就是同一个节点
问题便转化为:给定一个图,是否存在“一笔画”经过涂中每一点,每条边一次。
这样就是求图中是否存在欧拉路Euler-Path。
关于度数的判断方法:
节点的度用颜色出现次数来统计,如样例中,蓝色blue出现三次(不管是出度还是入度),那么blue结点的度就为3,同样地,我们也可以通过输入得到其他全部结点的度,于是,我们有:
Magenta=2
Blue=3
Red=2
Violet=1
Cyan=2
用一个一维数组就能记录了,然后分别模2,就能判断颜色结点的奇偶性
只要奇度数的结点数的个数=1或>=3,即使图连通,也一定不存在欧拉路径
Trie树+并查集+欧拉路径
#include<cstdio> #include<cstdlib> #define maxn 500010 typedef struct Trie{ int pos; struct Trie *next[26]; }Trie,*trie; trie root; int k=0,fa[maxn],d[maxn]; void Init(trie &p) { p=(trie)malloc(sizeof(Trie)); for(int i=0;i<26;i++) p->next[i]=NULL; p->pos=0; }//建Trie树,返回单词编号 int Build(trie &p,char *s,int depth) { if(!s[depth]){ if(p->pos) return p->pos; p->pos=++k; fa[k]=k,d[k]=0;//初始化祖先为自身,度数为0 return k; }//q不能定义为全局变量 trie q=p->next[s[depth]-'a']; if(q==NULL){ Init(q); p->next[s[depth]-'a']=q; } return Build(q,s,depth+1); } int Findset(int x) { if(fa[x]==x) return x; return fa[x]=Findset(fa[x]); } void Union(int u,int v) { int x=Findset(u); int y=Findset(v); if(x!=y) fa[x]=y; } int main() { Init(root); char s[12],t[12]; while(scanf("%s%s",s,t)!=EOF){ int x=Build(root,s,0); int y=Build(root,t,0); Union(x,y); d[x]++,d[y]++; } int scc=0,odd=0; for(int i=1;i<=k;i++){ if(fa[i]==i) scc++;//不连通 if(d[i]&1) odd++;//奇度节点 } if(scc>1||odd>2) puts("Impossible"); else puts("Possible"); return 0; }
离散化+并查集+欧拉路径
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 500010 using namespace std; int num[maxn],fa[maxn]; int idxc=1,idxw=1,cnt; struct Wood{ char left[12],right[12]; }wood[maxn/2]; struct Node{ char col[12]; bool operator<(const Node tmp)const{ if(strcmp(col,tmp.col)<=0) return true; return false; } }node[maxn]; //二分查找颜色对应离散的编号 int Binary_Search(char *col){ int l=0,r=cnt+1; while(r-l>1){ int mid=(r+l)/2; if(strcmp(node[mid].col,col)<=0) l=mid; else r=mid; } return l; } int Findset(int x) { if(fa[x]!=x) fa[x]=Findset(fa[x]); return fa[x]; } int main() { char s1[12],s2[12]; while(scanf("%s%s",wood[idxw].left,wood[idxw].right)!=EOF){ strcpy(node[idxc++].col,wood[idxw].left); strcpy(node[idxc++].col,wood[idxw].right); idxw++; } sort(node+1,node+idxc); memset(num,0,sizeof(num)); num[1]++,cnt=1; for(int i=2;i<idxc;i++){//进行离散处理,同时统计每种颜色出现的个数 if(strcmp(node[i].col,node[i-1].col)!=0){ strcpy(node[++cnt].col,node[i].col); num[cnt]++;//颜色不同保留下来 } else num[cnt]++;//颜色相同直接计数 } for(int i=1;i<maxn;i++) fa[i]=i; for(int i=1;i<idxw;i++){ int u=Binary_Search(wood[i].left); int v=Binary_Search(wood[i].right); int x=Findset(u),y=Findset(v); if(x!=y) fa[x]=y; } int scc=0,odd=0;//cnt是节点编号,也是节点个数 for(int i=1;i<=cnt;i++){ if(fa[i]==i) scc++; if(num[i]%2) odd++; } if(idxw==1||(scc==1&&(odd==0||odd==2))) printf("Possible\n"); else printf("Impossible\n"); }