SMZX十日遊(第二階段並查集)並查集學習筆記
創作背景
今天是在SMZX的第四天,終於學習新知識了,感動。所以,當然要寫部落格好好總結一番
並查集原理
近日,一款遊戲風靡世界,那就是:吃 雞 鴨。
為了取得勝利,就有許多玩家團結在一起,形成了多個非法組織。
眾所周知,很多組織的參與者都是被自己的朋友拉進來的,並且不能更換服飾,所以2個人見面時,為了不誤傷友軍,都要詢問自己的朋友發起者是誰才敢開打
int find(int k)
{
if(f[k]==k) return k;//如果是被自己召集進來的,那發起人就是自己
return find(f[k]);//如果不是就委託朋友找到拉他的朋友
}
如果這樣一個一個問,有可能很長時間都無法知道自己的組織,於是,\(路徑壓縮\)
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
// fa[x] = find(fa[x]);在搜尋組織的發起者時,就順帶記住自己的組織的發起者
// return fa[x];
}
這時候有一對V8 情侶,一不小心參加進不同的組織,為了讓他們在一起,官方讓兩個組織合併在一起,如果新創組織,那其他不知道這件事的雙方的組織的人可能就開打,於是便讓一個發起者服從另一個發起者,那誰服從誰呢?誰規模大就服從誰
void merge2(int x, int y) { int rx = find(x), ry = find(y);//尋找雙方的發起者 if (size[rx] > size[ry]) {//比較規模 fa[ry] = rx;//設定發起者 size[rx] += size[ry];//人數合併 } else { fa[rx] = ry; size[ry] += size[rx]; } }
其實這裡“吃鴨”遊戲,就已經涵蓋了並查集的基本操作,完整程式碼如下:模板題傳送門
#include<bits/stdc++.h> using namespace std; int f[10086],r,k,n,m,p1,p2,p3; int find(int k) { if(f[k]==k) return k; else return f[k]=find(f[k]); }//路徑壓縮 int main() { cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) { cin>>p1>>p2>>p3; if(p1==1) f[find(p2)]=find(p3); else if(p1==2) if(find(p2)==find(p3)) cout<<"Y"<<endl; else cout<<"N"<<endl; } return 0; }
小試牛刀
朋友
題目背景
小明在A公司工作,小紅在B公司工作。
題目描述
這兩個公司的員工有一個特點:一個公司的員工都是同性。
A公司有N名員工,其中有P對朋友關係。B公司有M名員工,其中有Q對朋友關係。朋友的朋友一定還是朋友。
每對朋友關係用兩個整數(Xi,Yi)組成,表示朋友的編號分別為Xi,Yi。男人的編號是正數,女人的編號是負數。小明的編號是1,小紅的編號是-1.
大家都知道,小明和小紅是朋友,那麼,請你寫一個程式求出兩公司之間,通過小明和小紅認識的人最多一共能配成多少對情侶。(包括他們自己)
輸入格式
第1行,4個空格隔開的正整數N,M,P,Q。
之後P行,每行兩個正整數Xi,Yi。
之後Q行,每行兩個負整數Xi,Yi。
輸出格式
一行,一個正整數,表示通過小明和小紅認識的人最多一共能配成多少對情侶。(包括他們自己)
輸入輸出樣例
輸入
4 3 4 2
1 1
1 2
2 3
1 3
-1 -2
-3 -3
輸出
2
說明/提示
對於30%資料,N,M<=100,P,Q<=200
對於80%資料,N,M<=4000,P,Q<=10000.
對於全部資料,N,M<=10000,P,Q<=20000。
思路
簡簡單單的並查集,首先搜尋兩間的朋友關係網。注意:由於女生是負數,所有要把負數轉為正數
然後:只要與小明小紅有關係的就統計下來,按最少的輸出畢竟沒有一夫多妻或一妻多夫
Code
#include<bits/stdc++.h>
using namespace std;
int ans=1,sum=1,f2[100005],f1[100005],p,q,n,m,p2,p3;
int find1(int k)
{
if(f1[k]==k || f1[k]==0) return k;
return f1[k]=find1(f1[k]);
}
int find2(int k)
{
if(f2[k]==k || f2[k]==0) return k;
return f2[k]=find2(f2[k]);
}
int main()
{
cin>>n>>m>>p>>q;
for(int i=1;i<=p;i++)
{
cin>>p2>>p3;
f1[max(find1(p2),find1(p3))]=min(find1(p3),find1(p2));//這裡如果把發起者算小,那麼後面查詢情侶會畢竟輕鬆,但大一個沒有問題
}
for(int i=1;i<=q;i++)
{
cin>>p2>>p3;
p2=0-p2;//把負數變成正數
p3=0-p3;
f2[max(find2(p2),find2(p3))]=min(find2(p3),find2(p2));
}
for(int i=2;i<=max(n,m);i++)
{
if(find1(f1[i])==1) ans++;//統計人數
if(find2(f2[i])==1) sum++;
}
cout<<min(ans,sum);//輸出
return 0;
}
THE END
其實,並查集……難度也還好吧
注:感謝這篇給我啟發的部落格