模板:並查集

(好的期末考試考砸的我滾回來寫代碼來了TAT)
(咦我什麼時候期末考試考好過~~)
卻說勇者有很多的親戚,親戚又有很多的親戚,親戚的親戚就是自己的親戚。
不幸的是,親戚中有些人不是很有錢,所以說會管勇者借錢。
勇者絲毫不介意啊,畢竟自從他討伐完大龍之後,他就日日有國王一個金幣的俸祿。
可是,終有一天,勇者發現一天之內,親戚就來了十多個。
還是路由器發現的,他將族譜整理完之後才告訴他說:“那些人有一半都不是你們家族的,就是過來詐騙的!”
勇者啞口無言,爲了拯救自己的金幣,他需要知道哪些人是自己的親戚。

——————————————

輸入格式:
第一行包含兩個整數N、M,表示共有N個人和M次操作。
接下來M行,每行包含三個整數Zi、Xi、Yi
當Zi=1時,表示Xi與Yi是親戚
當Zi=2時,輸出Xi與Yi是否是親戚,是的話輸出Y;否則話輸出N
輸出格式:
如上,對於每一個Zi=2的操作,都有一行輸出,每行包含一個大寫字母,爲Y或者N
提示:
親戚的親戚是親戚

——————————————

(題外話)
好的,我們接下來就是要講一個高深莫測玄幻之際的——並查集。
好吧在路由器沒學之前一直以爲並查集是huge佬所學的。
那麼今天將帶給蒟蒻也能看懂的並查集教程!
首先讓我們知道並查集的作用:簡短點說,就是需要完成的任務有1.合併集合2.查詢兩個元素是否在同一個集合內。
上面那句話一定要看懂再往下看(不然你會和路由器一樣當機半小時)
那麼,讓我們正式開始。

——————————————

勇者花費了一天的時間,終於找齊了所有七大姑八大姨了,那麼接下來就是需要靠古老魔法的幫助來完成族譜了。
路由器也在照相館中找到了魔法書,上面赫然寫着三個字。
並查集
“並查集的實現是類似於樹一般,一整棵樹代表了一整個集合。這樣做的好處是,我們要想知道元素是否在同一個集合內,只需要看他們所在的集合的根節點是否一樣即可。”
“但是,這並非意味着圖論,因爲鏈式前向星有很大的空間限制。”
“我們用一個很簡單的方法實現建樹——fa數組(指father,不要想象成奇怪的東西)”
“fa數組的作用,fa[i]=j表示j是i的爸爸。規定根節點的爸爸爲根節點。”
“這樣,當我們知道了要合併的兩個元素i,j時,找到i與j的根節點if,jf,我們就可以簡單粗暴的用fa[if]= jf來實現。”
“那麼就只有一個問題了,查詢。”
“查詢方法:即不斷遞歸,就是fa套fa套fa套fa……直到fa[i]=i,這時候我們就找到了所在集合的根節點了。”
“然後比較這樣得到的根節點即可,相等,即爲一個集合……”
勇者“啪”的一聲跑了出去,然後用自己的魔法代碼功力敲完了魔法。
(但是這個魔法有缺陷,因此不貼出啦!實際上是懶~)

然而有一天,勇者氣喘吁吁的找到了路由器。
“路由器,那個魔法……他,不管用了!”
“啊?怎麼回事?”路由器疑惑問道。
“額……我把這個魔法賣給了精靈族,但他們第二天就要求退貨,說不靈了。”
“不靈到不至於,怕不是太慢。”路由器懶洋洋地說,“那到底怎麼回事啊?”
“因爲精靈能活千多歲……所以,”勇者支吾道,“他們將自己輸入,再把爸爸輸入,再爺爺,再太爺爺,再太太爺爺,再太太太爺爺,再……”
“行了,我知道什麼問題了,”路由器拜拜手,“你還是把書好好看看吧!”
勇者拿起書,將最後一點問題看完。
“……然而,有的時候,這樣做出來的樹實際上只有幾個支鏈,有的甚至沒有支鏈,那麼找的速度就會明顯增加。”
“但是解決的辦法也很簡單——我們先查一遍,然後把所有沿途遇到的元素的爸爸(fa)全設爲根節點即可。”
“以下是本書附帶魔法代碼,如有不懂參看註釋。”

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int fa[10001]={0};
int find(int a){//找到a所在的集合的根節點 
    if(fa[a]!=a)fa[a]=find(fa[a]);
    //路徑壓縮優化
    //當我們找到根節點的時候
    //我們將這一路上我們往上爬的所有的節點
    //全部指向這個根節點,這樣再搜就會很快 
    return fa[a];
    //最開始的時候,我們碰到了第一個fa[a]==a
    //此時我們就知道了fa[a]爲根節點
    //將fa[a]的值傳遞給上一個fa[a],完成路徑壓縮 
    //經過壓縮之後到這個a,此時fa[a]就是根節點 
}
void judge(int a,int b){//判斷是否在同一個集合裏 
    if(find(a)!=find(b)){
        printf("N\n");
    }else{
        printf("Y\n");
    }
    return;
}
void unionn(int a,int b){//合併以a,b爲*根節點*與的兩個集合
    fa[b]=a;//簡單粗暴的將兩棵樹連接起來 
    return;
}
int main(){
    int n,m;//N個元素和M個操作
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        fa[i]=i;
        //初始化,將每一個集合視爲一棵樹
        //則開始時對於每一個元素,他的根節點就是他本身 
    }
    for(int i=1;i<=m;i++){
        int a1,b1,a,b,z;
        scanf("%d%d%d",&z,&a,&b);
        if(z==1){
            a1=find(a);
            b1=find(b);
            if(a1!=b1){//不是同一個集合 
                unionn(a1,b1);
            }
        }else{
            judge(a,b); 
        }
    }
    return 0;
}

下面附帶模板題:
洛谷 P3367 【模板】並查集:https://www.luogu.org/problem/show?pid=3367#sub
真的是模板,你只需要將上面的程序複製粘貼就AC啦!

發佈了59 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章