并查集
第十一届蓝桥杯第一场最后一题就是考的这个。
火星打架有个规矩,不能打自己人,什么叫做自己人呢,就是朋友的朋友就是朋友,那两个人碰到一起了,谁也不知道是不是自己人,所以每个群体都有一个大佬,把自己的大佬爆出来就知道是不是自己人了。有点像古惑仔里面的。
所以在并查集里一个人的人脉有两种:
大佬:这个群体的代表,能找得到大佬的都是自己人。
上级:如果我不是大佬,我的上级的上级的上级的…肯定是大佬。
并查集主要包括两个步骤:
1:找大佬
2:
实现
pre数组:pre[a]=b 表示 a 的上级是 b,初始情况下pre[i]=i,表示每个人都是一个帮派,然后通过后续的输入来令其中的人联系起来。
一:找大佬
大佬就是自己的上级是自己,即 pre[i]=i,所以就是,找自己的上级,然后找上级的上级,一直迭代下去,直到找到大佬:
int zhaodalao(int x){
int r;
r=x;
while(pre[r]!=r){
r=pre[r];
}
return r;
}
但是到这里就有个问题了,如果级数太多的话,每次打架都要问半个小时自己的大佬是谁,这就很不合适了啊,所以我们应该找到大佬之后,把沿途上的所有人的上级直接改成大佬,那下次这些人只要打一个电话就能找到大佬了啊。这个过程叫做状态压缩,更新后的代码如下:
int zhaodalao(int x){
int r;
r=x;
while(pre[r]!=r){
r=pre[r];
}
//到这里就已经找到x的大佬是r
//所以从x开始,把他的上级,上级的上级,上级的上级的上级...全都改成大佬 r
int help=x;
int z;
while(pre[help]!=help){
z=pre[help]; //因为状态压缩要把他的上级改成大佬,所以要先备份一下他的上级,以方便后续迭代。
pre[help]=r;
help=z;
}
return r;
}
二:加入某个大佬的帮派(把两个人联系在一起)
这个就很显然,先分别找到两个人的大佬,如果两个人的大佬是同一个人,则他们本来就有联系了,不用管他,要是两个大佬不同,则就可以让其中一个大佬的上级是另外一个大佬,这样的话就联系起来了撒:
void join(int x,int y){
int a,b;
a=zhaodalao(x);
b=zhaodalao(y);
if(a!=b){
pre[a]=b;
}
return;
}
整体代码
#include<stdio.h>
#include<stdlib.h>
#define MAX 1000
int n;
int pre[MAX];
int zhaodalao(int x){
int r;
r=x;
while(pre[r]!=r){
r=pre[r];
}
//到这里就已经找到x的大佬是r
//所以从x开始,把他的上级,上级的上级,上级的上级的上级...全都改成大佬 r
int help=x;
int z;
while(pre[help]!=help){
z=pre[help]; //因为状态压缩要把他的上级改成大佬,所以要先备份一下他的上级,以方便后续迭代。
pre[help]=r;
help=z;
}
return r;
}
void join(int x,int y){
int a,b;
a=zhaodalao(x);
b=zhaodalao(y);
if(a!=b){
pre[a]=b;
}
return;
}
int main(void){
n=520;
int i;
for(i=0;i<n;i++){
pre[i]=i;
}
return 0;
}