今天我主要是学习了一下并查集,发现并查集的东西虽然看起来难,但实际上理解之后就不是很难了。关于并查集问题的求解主要就是找“祖宗”以及遵循“靠左”原理,除此之外还有几个基本的函数。
//初始化函数
int d[20000]//定义一个数组存放祖宗编号
void start(int n)//n为总人数
{
for(int i=1;i<=n;i++){
d[i]=i;//将编号第i个人的祖宗初始化为自己,及没有祖宗
}
return;
}
//查询函数,运用递归找祖先
int find(int x)//x为人员编号
{
if(d[x]==x){//判断第x个人的祖先是否为自己,是则返回x值
return x;
}else{//如果不是,则找第x个人祖先的祖先,直至最大的祖先,然后返回
d[x]=find(d[x]);
return d[x];
}
}
//合并函数
void merge(int x,int y)//x,y均为人员编号
{
int t1,t2;//表示第x个人与第y个人的最大祖先
t1=find(x);
t2=find(y);//用查询函数
if(t1!=t2){//判断是否有共同祖先,不是则运用靠左原则,将祖先t1是为最大祖先,t2为下面的小辈
d[t2]=t1;
}
return;
}
下面给出一个基础的实际运用例题。
例题如下:
P3367 【模板】并查集
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。
接下来 M 行,每行包含三个整数 Zi,Xi,Yi 。
当 Zi=1 时,将 Xi 与 Yi 所在的集合合并。
当 Zi=2 时,输出 Xi 与 Yi 是否在同一集合内,是的输出 Y
;否则输出 N
。
输出格式
对于每一个 ��=2Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
输入输出样例
输入 #1复制
4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4
输出 #1复制
N Y N Y
说明/提示
对于 30%30% 的数据,N≤10,M≤20。
对于 70%70% 的数据,N≤100,M≤103。
对于 100%100% 的数据,1≤N≤104,1≤M≤2×105,1≤Xi,Yi≤N,i∈{1,2}。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int d[2000000];
void star(int n)//同上的初始化函数
{
for(int i=1;i<=n;i++){
d[i]=i;
}
return;
}
int find(int x)//找“祖先”关系函数
{
if(d[x]==x){
return x;
}else{
d[x]=find(d[x]);
return d[x];
}
}
void merge(int x,int y)//合并函数
{
int t1,t2;
t1=find(x);
t2=find(y);
if(t1!=t2){
d[t2]=t1;
}
return;
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);//输入
int a[m],b[m],c[m];
for(int i=0;i<m;i++){
scanf("%d %d %d",&a[i],&b[i],&c[i]);/输入
}
star(n);
for(int i=0;i<m;i++){
if(a[i]==1){//判断a[i]的情况,为1就进行合并,为2就判断是否在同一集合内
merge(b[i],c[i]);
}
if(a[i]==2){
if(find(b[i])==find(c[i])){
printf("Y\n");
}else{
printf("N\n");
}
}
}
return 0;
}
这题主要就是通过上面几个函数构建一个大致的关系图,然后在通过题目的要求去一步步判断,理解起来不难。要注意的就是当a[i]=2时,是不需要进行合并的,只需要判断。