给你一道题目:(可以看洛谷P3367)
给你编号为1~n的n个点,有m次操作:
操作有两种:
1 x y
将x所在的集合和y所在的集合合并
2 x y
如果x和y在同一个集合里,输出 Y,否则输出N
给一个样例:(就是洛谷上的,我懒得造样例了)
4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4
这就要用并查集求解了
首先:什么是并查集?
字面上的意思(相信看题也看得出来):并集和查集
因为集合是无序的,所以我们可以将它看成一棵树
很容易想到:当做1操作的时候,将x的父亲指向y
while(m--){
int p,x,y;
scanf("%d%d%d",&p,&x,&y);
if(p==1){
f[x]=y;//f[i]表示i的父亲
}
}
但这有个问题 ,就是只把x的父亲指向y只能保证x以及x的孩子与y合并
我们要做的是把x节点的根节点的父亲指向y的根节点的父亲
于是我们可以写一个find()函数
int find(int x){
if(f[x]==0)return x;//当x没有父节点,则x为根节点
return find(f[x]);//不是的话就继续搜索它的父节点
}
但如果节点编号是从0开始的呢?
所以我们就可以把f进行一个初始化,将f[i]赋值成i
for(int i=0;i<=n;i++)f[i]=i;//n是节点个数,将f[i]全部赋值成i
于是find()就可以这么写
int find(int x){
if(f[x]==x)return x;
return find(f[x]);
}
但如果树是这样的(本人图画得不好,见谅)
那么遍历就会变得很慢
所以可以加一个优化,就是每次遍历的时候把x的父节点提到x的根节点上去
int find(int x){
if(f[x]==0)return x;
return f[x]=find(f[x]);
}
最后,看一下最终的代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,f[10009];
int find(int x){
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1,p,x,y;i<=m;i++){
scanf("%d%d%d",&p,&x,&y);
int a=find(x);
int b=find(y);
if(p==1)f[a]=b;
else{
if(a==b)printf("Y\n");//如果x的根与y相同,即表明在统一集合
else printf("N\n");
}
}
return 0;
}