1. 程式人生 > >親戚【並查集】(內附並查集基本操作)

親戚【並查集】(內附並查集基本操作)

【問題描述】

  何氏家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。

【輸入格式】

  第一行:三個整數N,M,Q(N<=20000,M<=100000,Q<=100000),分別表示有N個人,M個親戚關係,詢問Q對親戚關係。
  接下來M行:每行兩個數x,y,1<=x,y<=N,表示x和y具有親戚關係。
  再接下來Q行:每行兩個數i,j,詢問i和j是否具有親戚關係。

【輸出格式】

  共Q行,每行一個’Yes’或’No’。表示第i個詢問的答案。

【輸入樣例】

9 8 3
1 3
1 6
3 6
2 4
2 8
8 4
5 8
9 7
1 6
2 5
3 7

【輸出樣例】

Yes
Yes
No

分析:
這道題從題目可以看出是“離線詢問”,故可以採用查詢強連通分量並標記每個點屬於哪個強連通分量的方法,在詢問時進行比較兩個點分別屬於哪個連通分量。
但在這裡主要介紹的方法是並查集,將每一個點看做集合的元素,如果他們存在親戚關係別將其分別隸屬的兩個集合合併,最終在詢問時判斷兩點是否屬於同一集合便可。這種演算法的優秀之處在於可以解決“線上詢問”的問題。

並查集程式碼實現如下:

#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 20005
using namespace std;
int n,m,q,pa[maxn];
void initial(){
     for(int i=1;i<=n;i++)
         pa[i]=i;
}
int find(int x){
    if(pa[x]==x)return x;
    return find(pa[x]);
}
void Union(int x,int y){
     pa[find(x)]=find(y);
}
void
init(){ scanf("%d%d%d",&n,&m,&q); } int main(){ init(); initial(); int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Union(x,y); } int p,k; for(int i=1;i<=q;i++){ scanf("%d%d",&p,&k); if(find(p)==find(k))printf("Yes\n"); else printf("No\n"); } return 0; }

在本文的最後,附上並查集的四個基本運算:

void initial(){              //初始化
     for(int i=1;i<=n;i++)
         pa[i]=i;
}
int find(int x){              //查詢  核心操作
    if(pa[x]==x)return x;
    int root=find(pa[x]);
    pa[x]=root;                    //深度壓縮
    return root;
}
void Union(int x,int y){             //合併
     pa[find(x)]=find(y);
}
bool judge(int x,int y){
     return find(x)==find(y);
}