1. 程式人生 > 實用技巧 >洛谷P1525 —— 帶權值並查集

洛谷P1525 —— 帶權值並查集

題目連結:https://www.luogu.com.cn/problem/P1525

題目描述

S 城現有兩座監獄,一共關押著N名罪犯,編號分別為1N。他們之間的關係自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。我們用“怨氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為c的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為c的衝突事件。

每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成一個列表,然後上報到 S 城 Z 市長那裡。公務繁忙的 Z 市長只會去看列表中的第一個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。

在詳細考察了N名罪犯間的矛盾關係後,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。

那麼,應如何分配罪犯,才能使 Z 市長看到的那個衝突事件的影響力最小?這個最小值是多少?

輸入格式

每行中兩個數之間用一個空格隔開。第一行為兩個正整數N,M分別表示罪犯的數目以及存在仇恨的罪犯對數。接下來的M行每行為三個正整數aj,bj,cj,表示aj號和bj號罪犯之間存在仇恨,其怨氣值為cj。資料保證1<ajbjN,0<cj1e9,且每對罪犯組合只出現一次。

輸出格式

1行,為 Z 市長看到的那個衝突事件的影響力。如果本年內監獄中未發生任何衝突事件,請輸出0

程式碼:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <cstdio>
 5 
 6 using namespace std;
 7 
 8 const int N = 20010, M = 1e5 + 10;
 9 struct it
10 {
11     int a, b, w;
12 }its[M];
13 int n, m;
14 int p[N]; 15 int d[N]; 16 17 bool cmp(it x, it y) 18 { 19 return x.w > y.w; 20 } 21 22 int find(int x) 23 { 24 if (p[x] != x) p[x] = find(p[x]); 25 return p[x]; 26 } 27 28 int main() 29 { 30 scanf("%d%d", &n, &m); 31 for (int i = 1; i <= n; i ++ ) p[i] = i; 32 for (int i = 1; i <= m; i ++ ) 33 { 34 int a, b, w; 35 scanf("%d%d%d", &a, &b, &w); 36 its[i] = {a, b, w}; 37 } 38 39 sort(its + 1, its + 1 + m, cmp); 40 41 int res; 42 for (int i = 1; i <= m + 1; i ++ ) 43 { 44 int fa = find(its[i].a), fb = find(its[i].b); 45 if (fa == fb) 46 { 47 res = its[i].w; 48 break; 49 } 50 else 51 { 52 if (!d[its[i].a]) d[its[i].a] = its[i].b; 53 else 54 { 55 int f = find(d[its[i].a]); 56 p[fb] = f; 57 } 58 if (!d[its[i].b]) d[its[i].b] = its[i].a; 59 else 60 { 61 int f = find(d[its[i].b]); 62 p[fa] = f; 63 } 64 } 65 } 66 67 cout << res << endl; 68 return 0; 69 }

本題,因為說了有“邊權值”(我理解為矛盾值),所以要求出現矛盾情況下的最小邊權值顯然是需要排序的

那麼問題又來了,我們要按照什麼方法進行分配呢?

我們不妨這樣想:兩個人a,b有仇,那麼把他們放在一起顯然會打起來,那麼我們還不如把a與b的其他敵人放在一起,

因為這樣可能會出現“敵人的敵人就是朋友”的情況,恰好a與b的其他敵人之間沒有矛盾,那麼他們就可以放在同一個集合中,反之b對a亦然。

那麼我們不妨這樣實現: 首先需要並查集初始化

(1)先把所有的矛盾關係按照矛盾值從大到小排一遍序,

(2)接下來每次操作取出一個關係,看矛盾的兩個人x和y是否已經分配到同一個集合中(並查集找父親即可),那麼還分如下兩種情況:

如果在一起那麼顯然會打起來(會出現矛盾),那麼直接輸出當前的邊權(矛盾值)即可(此時保證是最小矛盾值,因為已經排序了)

如果不在同一組,則按照“敵人的敵人就是朋友”的原則,把x與y的其他敵人分在同一組,y與x的其他敵人分在同一組

不斷進行以上操作最終可以得到答案