1.并查集简介
若干点,有连接关系(可传递)的结点为一个集合,每个点都有一个父结点,可追溯到一个父结点上。
2.追溯祖结点
查找每个结点的祖结点,可用于判断两个点是否有连接关系,祖结点同便是有连接关系
int find(int x) { int r=x; //r的上级如果是自己那么就是祖结点 while (pre[r ]!=r) r=pre[r ] ; return r ; }
3.集合建立联系
两个点建立联系会使两个集合建立联系,使两个祖结点建立联系便可以使两个集合全部建立联系,因为此时只有一个祖结点了,两个集合共一个祖结点。
void join(int x, int y) { int fx = find(x),fy = find(y); //祖结点不同时,x加入y if(fx != fy) pre[fx] = fy; }
4.路径压缩算法
为了提高查找效率,尽可能让每个结点的父结点为祖结点,便实现压缩(可替代find())
//修改查找函数,在查找中,将结点的一个个父结 点更改为祖结点 int Find(int x) { int r = x; //找到祖结点 while(r != pre[r]) r = pre[r]; int i = x,j; //把x的父结点的父节点等等的父节点设为祖结点 while(pre[i]!=r) { j = pre[i]; pre[i] = r; i = j; } return r; }
5.模板题———家族(vijos)
描述若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
格式输入格式第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
样例1样例输入16 5 31 21 53 45 21 31 42 35 6样例输出1YesYesNo
AC源码:
using namespace std; int pre[5000], n,m,p,a,b; int main() { cin>>n>>m>>p; for(int i = 0;i<n;i++) pre[i] = i; for(int i=0;i<m;i++) { cin>>a>>b; join(a,b); } for(int i = 0;i<p;i++) { cin>>a>>b; printf("%s\n",Find(a)==Find(b)?"Yes":"No"); } return 0; }