一种查找祖先的方法——并查集
我们先来看这么一个问题:
第一行包含两个整数\(N、M\),表示共有\(N\)个元素和\(M\)个操作。
接下来\(M\)行,每行包含三个整数\(Zi、Xi、Yi\)
当\(Zi=1\)时,将\(Xi\)与\(Yi\)所在的集合合并
当\(Zi=2\)时,输出\(Xi\)与\(Yi\)是否在同一集合内,是的话输出\(Y\);否则话输出\(N\) 传送门(luogu)3397
对于这种问题,我们首先想到的是暴力也许是我太弱了
但是,我们容易发现,由于这题的数据范围,暴力显然是不被接受的,于是,我们需要引入一个新的算法——并查集
再讲这道题之前,我们先引一个比较有名的例子:
有a,b,c三个人
假设a和b打架了,a做了b的小弟。则令f[a]=b;
后来a打赢了c 黑社会
那么c就是a的小弟了。所以,令f[c]=a;
但是,c不知道b,这不符合要求。
所以,我们需要让c知道a,于是我们就定义一个\(find\)函数:
int find(int k) {
if(f[k]==k) return k;
else return find(f[k]);
}
于是,我们就能写出这么一个代码
#include<iostream>
#include<cstdio>
using namespace std;
int a[10003];
int n,m;
int tot,x,y;
int read() {
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') f=-f;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int find(int k) {
if(a[k]==k) return k;
else return find(a[k]);
}
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++) {
a[i]=i;//现将自己定为自己的老大
}
for(int i=1;i<=m;i++) {
tot=read(),x=read(),y=read();
if(tot==1) {
a[find(x)]=a[y];//将自己的老大变成$y$的老大(x赢了y)
}
else {//判断
if(find(x)==find(y)) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
}
但是,这会出现问题,为什么?
我们需要加入这么一个式:a[k]=find(a[k])(我们要重新赋值a[k],不然除非\(find\)函数赋值,f数组不变

浙公网安备 33010602011771号