並查集:
並查集說的是將一些元素分爲幾個集合,每個集合各自有自己的共同特點,當然便於查詢,每個集合都必須有各自的標誌(編號),我們可以通過這個標誌來查詢集合裏的元素,判斷元素是否屬於這個集合,總共分爲了多少個集合。於是,我們選擇了樹形結構存每個集合的元素。某棵樹的根相當於這個集合的標誌。有幾棵樹就有幾個集合。判斷兩個元素是否屬於同一個集合,就看它們的根是否一致(用遞歸可查詢它們的根)。
基本操作代碼:
初始化:
void init()
{
for(int i=1;i<=n;i++)
{
root[i]=i;//初始時,每個元素都是一顆以自己爲根的樹
rank[i]=0;//樹的高度
}
}
找根:
int find_root(int v)
{
if(root[v] != v)
{
root[v]=find_root(root[v])//路徑壓縮,root[v]經過遞歸之後最終就會是此時v所在的樹的根
}
return root[v];
}
合併:
void unite(int x,int y)
{
int rx=find_root(x);
int ry=find_root(y);
if(rx==ry) return;//以前就屬於同一棵樹
if(rank[rx]>rank[ry])
{
root[ry]=rx;//將根爲ry的那棵樹變爲根爲rx的子樹,由於rank[ry]+1<=rank[rx],所以rank[rx]不需要變;
}
else if(rank[rx]==rank[ry])
{
root[ry]=rx;
rank[rx]++;
}
else
{
root[rx]=ry;
}
}
學並查集的收穫:
給集合標號;找“根”的方法;路徑壓縮的方法;
注意事項:
經過一系列的合併(unite)操作之後,root[i]還不一定是i所在樹的根,可能只是這顆樹下的子樹的根,所以要求它的根還需find_root(i)。
例題:
HDU1829 A Bug’s life
題意:有一些小蟲子,蟲子裏面可能有同性戀,一位博士通過觀察它們的交配情況,來判斷是否有同性戀,若能判斷出來有,則輸出Suspicious bugs found! 否則輸出No suspicious bugs found!
分析:這是一道經典的並查集,由於題目告訴了你它們的交配情況,我們以他們的性別爲劃分集合的依據,則互相交配的蟲子分屬不同的集合,即它與和它交配的蟲子的反面在一個集合內。
代碼如下:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int b_f[5000];
void init(int n)
{
for(int i=1;i<=n;i++)
{
b_f[i]=i;
}
}
int f_root(int a)
{
if(b_f[a]!=a)
b_f[a]=f_root(b_f[a]);
return b_f[a];
}
void Union(int x,int y)
{
int fx=f_root(x);
int fy=f_root(y);
b_f[fx]=fy;
}
int main()
{
int cnt;
int m,n;
scanf("%d",&cnt);
int co=0;
while(scanf("%d %d",&m,&n))
{
if(cnt==0) break;
cnt--;
co++;
int a,b;
bool ju=true;
init(2*m);
for(int i=0;i<n;i++)
{
scanf("%d %d",&a,&b);
//cin>>a>>b;
if(!ju) continue;
if(f_root(a)!=f_root(b))
{
Union(a,b+m);//a與b的反面放在同一個集合中
Union(b,a+m);
}
else
ju=false;
}
if(!ju)
printf("Scenario #%d:\nSuspicious bugs found!\n\n",co);
else
printf("Scenario #%d:\nNo suspicious bugs found!\n\n",co);
}
return 0;
}