1. 程式人生 > >做題記錄:P1525 關押罪犯(洛谷)

做題記錄:P1525 關押罪犯(洛谷)

朋友 不同的 cst 答案 log 表示 應該 如何 結束

P1525 關押罪犯

/*在這一道題中並查集的作用是:將同一個監獄裏的罪犯合並到一起。
思路:將每對罪犯之間的怨氣值從大到小排序,再依次把他們分到不
同的兩個監獄裏,當發現這一對罪犯已經在同一個監獄裏時,就說明
他們已經不能再分開了(分開了就不是最優了)。此時,這一對罪犯
之間的怨氣值就是答案。值得註意的是,當沒有沖突發生時,要記得
輸出0。那麽,我們應該如何用並查集實現以上的思路呢?首先說幾個
關鍵詞語的含義:
1、“敵人”:如果一對罪犯被分到兩個不同的監獄裏,那麽他們就互為“敵人”。
2、“朋友”:如果一對罪犯被分到一個監獄裏,那麽他們就互為“朋友”。
需要明確的一點是“敵人”的“敵人”,就是“朋友”。
當分開一對罪犯時(假設他們的名字叫x和y),若x還沒有“敵人”,那麽y就
是他的“敵人”(因為他們被分開了嘛);否則就把y所在的集合與x的“敵人”
所在的集合合並,因為x是y的“敵人”,所以x的“敵人”就是y的“敵人”的
“敵人”(即朋友),將y所在的集合與x的“敵人”所在的集合合並是因為x的
“敵人”和x關在不同的監獄裏,而y又和x關在不同的監獄裏,並且只有兩個監
獄,所以x的“敵人”所在的集合裏面的人和y所在的集合裏面的人一定關在一
個監獄裏。對y的“敵人”也這樣處理一遍就可以了。
*/ #include<iostream> #include<cstdio> #include<algorithm> using namespace std; struct edge//存罪犯之間的怨氣關系 { int u; int v; int w; }e[100005]; bool cmp(edge x,edge y)//將怨氣值從大到小排序的排序函數 { return x.w>y.w; } //f為用作實現並查集的數組,表示的是第i個罪犯的“祖先”是誰,enemy儲存的是第i個罪犯的某個敵人 int f[20005
],enemy[20005]; int find_(int x)//並查集查找函數 { if(f[x]==x) return x; return f[x]=find_(f[x]); } void merge_(int x,int y)//並查集合並函數 { int t1=find_(x),t2=find_(y); if(t1!=t2) f[t2]=t1; return; } int main() { int n=0,m=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) f[i]=i;//
並查集初始化 for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e+1,e+m+1,cmp);//將怨氣值從大到小排序 for(int i=1;i<=m;i++) { if(find_(e[i].u)==find_(e[i].v))//如果這一對罪犯已經在同一個監獄裏 { printf("%d",e[i].w);//直接輸出他們之間的怨氣值 return 0;//結束程序 } //如果這一對罪犯仍能分開 if(!enemy[e[i].u]) enemy[e[i].u]=e[i].v;//如果u還沒有敵人,那麽v就是他的敵人 else merge_(e[i].v,enemy[e[i].u]);//否則,就將v與u的敵人合並 if(!enemy[e[i].v]) enemy[e[i].v]=e[i].u;//如果v還沒有敵人,那麽u就是他的敵人 else merge_(e[i].u,enemy[e[i].v]);//否則,就將u與v的敵人合並 } printf("%d",0);//記得輸出0 return 0; }

做題記錄:P1525 關押罪犯(洛谷)