做了並查集一段時間了。個人覺得利用並查集解題的套路其實很單調。
1、開一個數組記錄各個節點的父節點,初始化
2、給出一對關聯的資料,尋找
3、根據尋找結果如果根不屬於同一集合則合并
當然,還可以最佳化。主要是在尋找利用遞迴,使得在回溯時各個節點的父節點都是樹的根節點。下次尋找就可以
降低尋找長度。其次,可以利用一個數組記錄每棵樹的高度。在合并時將矮樹連結到高樹上,使得新產生的樹盡
量矮。
大家可以看看這兩個連結:點擊開啟連結 和點擊開啟連結
我做這題的想法:
1、在合并兩棵樹時一般要求是根節點不相同才合并。其實,我覺得這個要求也是可以根據實際情況來看的。
如果只是比較單純的並查集問題可以相同也合并,合并前後也沒有啥變化。但是比如本題就必須是不同才合并。
相同也合并會對num[]造成影響。
2、注釋掉的代碼是用來在合并時將矮樹連結到高樹上用的。可是空間不夠了。
大家在看代碼時可能會發現這樣一個問題,以a為根的樹連結到以b為根的樹上後,height[a]應該賦值為0。代碼中
並沒有這樣做。因為不處理也不影響。為啥呢?因為a已經不再是根了。即使下次要處理一組關聯的邊(a,c)時,也
是用a的根和c連結。也就是說a不會再成為根,height[a]也不會再被用了。
AC代碼:
#include<iostream>using namespace std;const int MAX=10000001;int father[MAX]; //father[i]存放i的父節點int num[MAX]; //num[i]以i為根的節點數//int height[MAX]; //樹的高度void initial() //初始化{for(int i=0;i<MAX;i++){father[i]=i;num[i]=1; //每個i都是根節點,節點數都為1//height[i]=1;}}int find(int a) //尋找{if(a==father[a])return a;return father[a]=find(father[a]); //路徑壓縮}void combine(int a,int b) //不在同一集合就合并{int tmpa=find(a);int tmpb=find(b);if(tmpa!=tmpb){father[tmpa]=tmpb;num[tmpb]+=num[tmpa];num[tmpa]=0;/*if(height[tmpb]>height[tmpa]) //每次合并都使樹的高度盡量小{father[tmpa]=tmpb;num[tmpb]+=num[tmpa];num[tmpa]=0;}else{father[tmpb]=tmpa;num[tmpa]+=num[tmpb];num[tmpb]=0;if(height[tmpa]==height[tmpb])height[tmpa]++;}*/}}int maxNode() //連通分量最大節點數{int tmp=0;for(int i=0;i<MAX;i++){if(num[i]>tmp)tmp=num[i];}return tmp;}void preDeal(int n) //預先處理{int a,b; for(int i=0;i<n;i++){scanf("%d%d",&a,&b);combine(a,b);}}int main(){int n;while(scanf("%d",&n)==1){initial();preDeal(n);printf("%d\n",maxNode());}return 0;}