【图论_并查集】
一种特殊的数据结构——并查集
并查集的思想主要用来找一些数据之间的关系
或许你并不知道,你的某个朋友是你的亲戚,他可能是你曾祖父的外公的女婿的外甥女的表姐的孙子。所以为了解决你周围的人和你是不是有亲戚关系,我们就需要并查集的思想qwq。
思想:合并集合的思想,可以理解为找共同的祖先,在数学上或许可以看成把同源的人合并到一个集合里:
(找不到图感性理解吧)
总之概括一下就是一个合并查找的过程。(通俗一点:“找祖宗”)
下面来看一个eg:
算法就一个“并查集”(孤零零),是很典型的例题了。
思路:
初始状态,每个人的父亲都是他自己qwq
for(int i=1;i<=n;i++) father[i]=i;
开始输入两个人的亲戚关系:
1.寻找他们的祖先并合并
如果他们不是同一个祖先,因为他们有亲戚关系,故把他们合并成一个集合
int find_father(int x){//寻找祖先,递归操作 if(father[x]!=x) return find_father(father[x]); return x; } /*int find_father(int x){//进行优化,使每个人的“father”直接指向”老祖宗“,避免重复递归的时间 if(father[x]!=x)father[x]=find_father(father[x]); return father[x]; }*/ void unionn(int x,int y){//合并两个集合(因为通过不断”找father“,一定可以判断他们是一个集合中的,这里规定谁是谁的father看习惯) father[y]=x; } for(int i=1;i<=m;i++){ cin>>mi>>mj;//输入某两个人的亲戚关系 int r1=find_father(mi);//找mi的祖先 int r2=find_father(mj);//找mj的祖先 if(r1!=r2)unionn(r1,r2);//如果他们祖先不同,那么就合并(避免重复合并 }
查:查找两个人是否有亲戚关系:
for(int i=1;i<=p;i++){ cin>>pi>>pj; if(find_father(pi)==find_father(pj)) cout<<"Yes"<<endl; else cout<<"No"<<endl; }
我们把代码也并查集一下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int n,m,p,mi,mj,pi,pj; int father[5001]; void unionn(int x,int y){ father[y]=x; } int find_father(int x){ if(father[x]!=x)father[x]=find_father(father[x]); return father[x]; } int main(){ scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=n;i++) father[i]=i; for(int i=1;i<=m;i++){ cin>>mi>>mj; int r1=find_father(mi); int r2=find_father(mj); if(r1!=r2)unionn(r1,r2); } for(int i=1;i<=p;i++){ cin>>pi>>pj; if(find_father(pi)==find_father(pj)) cout<<"Yes"<<endl; else cout<<"No"<<endl; } }
end-