No.7.2 並查集演算法
暫時沒看懂,只做記錄,後續再究!
一、求有幾個強盜同夥?
現在有10個強盜:
1與2是同夥; 3與4是同夥;
5與2是同夥; 4與6是同夥;
2與6是同夥; 8與7是同夥;
9與7是同夥; 1與6是同夥;
2與4是同夥;
強盜同夥的同夥,也是同夥。請問共有幾個獨立的強盜同夥?
解題思路:
1.首先假設10個強盜各不相屬,用一維陣列表示 f[i] = i
2.合併同夥:如果兩人是同夥,以靠左原則為準,比如:“5與2是同夥,左邊是5,那麼2從屬於5” <=>
1). u,v ( f[u]=u, f[v]=v ), 靠左原則 f[v]=u;
2). w,v( f[w]=w, f[v]=u ), "擒賊先擒王"原則,u,v 都要從屬於 w, 即 f[v]=w; f[u]=w;
3). v, x ( f[v]=w, f[x]=x ), x 從屬於 v,f[x] = f[v] = w;
上述過程即並查集演算法:每個點開始都是隻有一個節點的樹,通過一些條件合併成一顆大樹,合併過程要遵循靠左原則和擒賊先擒王原則,找到相同的父節點和根節點。
二、code
int t[100]={0},n,m;
//初始化,t[i]=i,表示每個點都是自己的父節點,相互之間沒有關係
void init(){
int i;
for(i=1;i<=n;i++){
t[i]=i;
}
}
int getf(int v){ // 尋找根節點
if(t[v] == v)
return v;
else{
t[v]=getf(t[v]); // v 是一維陣列的索引座標,t[v] 才是其父節點,所以 t[v] = getf( t[v] ); 在遞迴過程中,中間節點的父節點也被更新
return t[v]; // 返回值是父節點,不是 v
}
}
/* 其實這個函式用一句就可以代替:t[ getf(y) ] = t[ getf(x) ];
void merge(int x,int y){
int t1,t2;
t1=getf(x);
t2=getf(y);
if(t1!=t2){
t[t2]=t1;
}
}
*/
int main(){
int i,a,b;
int num=0;
scanf("%d %d",&n,&m);
init();
for(i=1;i<=m;i++){
scanf("%d %d",&a,&b);
t[ getf(b) ] = t[ getf(a) ];
}
for(i=1;i<=n;i++)
printf("%d ",t[i]);
for(i=1;i<=n;i++){
if(t[i]==i) // 在假設的前提下,只有根節點的值不變,可以求出群的個數
num++;
}
printf("\n%d\n",num);
getchar();getchar();return 0;
}
三、程式碼不大,演算法也不難,關鍵是怎麼分解、實現。這是才是入門的基礎!