【并查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例题详解
不想看模板,想直接看题目的请戳下面目录:
目录:
poj 2236 wireless network 【传送门】
poj 1703 find them, catch them 【传送门】
先上模板:
1 #define maxn 根据编号需要 2 int per[maxn],rank[maxn]; 3 4 void init(int n) 5 { 6 int i; 7 for(i=1;i<=n;i++) 8 { 9 per[i]=i;rank[i]=0 10 } 11 12 } 13 14 int find(int x) 15 { 16 if(x==per[x]) 17 return x; 18 return per[x]=find(per[x]); 19 } 20 21 void unite(int x,int y) 22 { 23 x=find(x); 24 y=find(y); 25 if(x==y) 26 return; 27 if(rank[x]>rank[y]) 28 per[y]=x; 29 else 30 { 31 per[x]=y; 32 if(rank[x]==rank[y]) 33 rank[y]+=1; 34 } 35 } 36 37 bool same(int x,int y) 38 { 39 return find(x)==find(y); 40 }
并查集详解请访问:(通俗易懂解释)
接下来来看几个例子:
hdu 1213 how many tables
题目大意:有一个人过生日,请到了他的诸多朋友,但是这些朋友之间有的认识,有的不认识。这个人想尽可能的把相互之间认识的人凑到一张桌子上,不认识的人则去另一张桌子。朋友们互相认识的规则是:比如a认识b,b认识c,那么a,b,c就可以凑到一桌子上。现在问:他的朋友们以这样的规则能凑够几桌。
解题思路:运用并查集,把相互认识的人都联合一下,到最后计算数组中有几个per值等于自身的人就可以了。
hdu 1232 畅通工程
此处略去题目大意……
解题思路:其实这个题目和上一个题异曲同工,就是变换了一下条件,我们假设村子是上题的朋友,那么路就是朋友们之间的关系,那么两堆朋友之间连线,其实只需要一次就可以了,同样,三堆朋友之间要想相互认识,只需要两个关系就可以了,所以我们很容易可以得出朋友们要想都认识,只需要把朋友堆数 - 1 就可以了。
代码只需要将上题的输出结果-1即可 ,不再贴出。
poj 2236 wireless network
题目大意:一片废弃的地方,acm协会正在进行救援,可是所有电脑的通信设施都被破坏了,被修复好的电脑的信号传输距离只有d米,现在给出所有电脑的坐标,然后给出指令s与o,s后跟两个数字代表两个电脑的编号,询问这两台电脑是否能通信,如果能则输出success,否则输出fall;o后跟一个编号,代表修好一定电脑。
解题思路:此题运用并查集,在修复好一台电脑之后遍历所有修好的电脑,如果两点间距离小于d米,则连通两个点;遇到s的时候只需要判断是否连通即可。
poj 1703 find them, catch them
题目大意:有两个帮派,警察现在抓住了n个罪犯,输入字母a或d,后面跟着两个罪犯的编号。如果字符是a,则代表这一次询问:如果不是一个帮派,则输出“in different gangs.”;是一个帮派输出"in the same gang.";否则输出“not sure yet.”。如果是字符d,那就代表着后面的两个编号不属于一个帮派!
解题思路:需要一个标记数组vis,用来标记两个罪犯(a,b)不属于同一个帮派,如果标记数组两个罪犯都为0则代表不确定是哪个帮派,并且让vis[a]=b,vis[b]=a;如果vis[a]==0&&vis[b]!=0,则让vis[b]=a,并且连通a,vis[b];如果vis[b]==0&&vis[a]!=0,则让vis[a]=b,并且连通b,vis[a];最后一种情况是a与b的标记数组都不为0,那么连2019-07-21通a,vis[b],连通b,vis[a]最后判断即可。
下一篇: 说说自信 生活工作