【資料結構】——並查集
阿新 • • 發佈:2019-01-22
概念:所謂並查集,就是把一個集合合併再進行查詢。
並查集基本操作:
1、將a、b兩個元素合併在一個集合
2、查詢a、b是否在一個集合
那麼,我們這是就要思考:如何將每個元素合併在一個集合呢?它們之間是否存在著某個標誌?
很顯然,標誌是肯定要存在的。
首先,我們可以將幾個元素組成的集合看成一棵樹,每個元素就是一個節點。那麼我們在這裡就需要藉助一下樹的特性:每個點都有祖先節點。既然如此,我們便可以把標誌轉移到各個元素的祖先節點上:
fa[x]表示元素x的父親節點,若想找其祖先節點,只需要一直遞迴下去便可
那麼首先就把每個元素的父親節點定為自己
那麼並查集有以下幾段優美的程式碼:
查詢公共祖先
int getfa(int k){
if(k==fa[k]) return k;
fa[k]=getfa(fa[k]);
return fa[k];
}
合併
x=getfa(x),y=getfa(y);
if (x!=y) fa[y]=x;
判斷是否在同一個集合
bool panduan(int a,int b){
return(getfa(a)==getfa(b));
}
//好吧也許我寫得不夠優美
那麼上幾道例題:
一、親戚
題目描述:有n個人,m個關係,k個查詢。2到k行,a、b,說明a、b為親戚(間接關係也算);k+1到最後,x、y,查詢它們是否為 親戚關係
題意分析:並查集的一道模板題。將a、b合併在一個集合,就無需考慮間接關係;那麼查詢時只需要判斷x、y是否在同一集合便可
那麼程式碼如下:
#include<bits/stdc++.h> using namespace std; int fa[1000001]={},n,m,p; int getfa(int k){ if(k==fa[k]) return k; fa[k]=getfa(fa[k]); return fa[k]; }//查詢祖先節點 int main(){ cin>>n>>m>>p; for (int i=1;i<=n;i++) fa[i]=i; for (int i=1;i<=m;i++){ int x,y; cin>>x>>y; x=getfa(x),y=getfa(y); if (x!=y) fa[y]=x;//如果不在同一集合,便合併,使他們成為親戚 } for (int i=1;i<=p;i++){ int x,y; cin>>x>>y; int xx=getfa(x),yy=getfa(y); if (xx==yy)cout<<"Yes"<<endl;//如果在同一個集合,說明是親戚 else cout<<"No"<<endl;//不然擇不是 } return 0; }
二、犯罪團伙
題意描述:有a、b兩個人,他們要麼是朋友,要麼是敵人。 而且有一點是肯定的: a的朋友的朋友是a的朋友; a的敵人的敵人也是a的朋友。 兩個強盜是同一夥的當且僅當他們是朋友。現在小明同學想知道有多少個犯罪同夥
題目分析:這道題我開始做的時候也是毫無思路,但仔細讀了幾遍題目後,就知道了一個重點——敵人的敵人就是朋友!那麼這樣我們就可以用一個數組f[x]儲存x的敵人。如此,我們就可以把敵人的敵人合併在一個集合中,即是朋友,在一個團伙
合併完後,我們便可以查詢有幾個團伙
那麼程式碼如下:
#include<bits/stdc++.h>
using namespace std;
int a[1000001]={},fa[1000001]={},n,m;
int getfa(int k){
if (k==fa[k]) return k;
fa[k]=getfa(fa[k]);
return fa[k];
}
int main(){
cin>>n>>m;
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=m;i++){
int x,y;
char ch;
cin>>ch;
cin>>x>>y;
if (ch=='F'){
int xx,yy;
xx=getfa(x);yy=getfa(y);
if (xx!=yy) fa[yy]=xx;//如果是朋友,直接合並
}
else{
int xx,yy;
if (a[x]==0)a[x]=y;
if (a[y]==0)a[y]=x;//敵人的敵人是朋友,記錄敵人的敵人
xx=getfa(x);yy=getfa(a[y]);//把自己的祖先和敵人的敵人的祖先找到
if (xx!=yy) fa[yy]=xx;//合併
xx=getfa(y);yy=getfa(a[x]);//同樣
if (xx!=yy) fa[yy]=xx;//合併
}
}
bool f[100001]={};
int s=0;
for (int i=1;i<=n;i++){
int x=getfa(i);
if (!f[x]) s++,f[x]=1;//如果祖先沒出現過,說明是新的一個集合,就加一
}
cout<<s;//輸出
return 0;
}