NOIP2010關押罪犯 補集
Description
S城現有兩座監獄,一共關押著N名罪犯,編號分別為1~N。他們之間的關係自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。我們用“怨 氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為 c 的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為c的衝突事件。
每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成一個列表,然後上報到S城Z市長那裡。公務繁忙的Z市長只會去看列表中的第一個事件的影響力, 如果影響很壞,他就會考慮撤換警察局長。
在詳細考察了N名罪犯間的矛盾關係後,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。那麼,應如何分配罪犯,才能使Z市長看到的那個衝突事件的影響力最小?這個最小值是多少?
Input
輸入檔名為 prison.in。輸入檔案的每行中兩個數之間用一個空格隔開。
第一行為兩個正整數N和M,分別表示罪犯的數目以及存在仇恨的罪犯對數。
接下來的M行每行為三個正整數 aj,bj,cj,表示aj號和bj號罪犯之間存在仇恨,其怨氣值為cj。資料保證1≤aj
Output
輸出檔案prison.out共1行,為Z市長看到的那個衝突事件的影響力。如果本年內監獄 中未發生任何衝突事件,請輸出0。
Sample Input
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
Sample Output
3512
Hint
【樣例說明】
罪犯之間的怨氣值如下面左圖所示,右圖所示為罪犯的分配方法,市長看到的衝突事件 影響力是3512(由2號和3號罪犯引發)。其他任何分法都不會比這個分法更優。
【資料範圍】
對於30%的資料有N≤15。
對於70%的資料有N≤2000,M≤50000。
對於100%的資料有N≤20000,M≤100000。
TJ:
用並查集表示每個犯人之間的關係,在同一個集合中則說明兩人在同一個監獄,反之則不在同一個監獄。
然後按照仇恨值的大小從大到小處理,這裡把資料縮小以便觀察,最後一列為怨氣值。
1 2 6
3 4 5
1 3 4
2 3 3
1 4 2
2 4 1
對於一組資料,表示a和b的怨氣值,那麼將犯人a和犯人b的補集元素(b+n)放在一個集合中,以此這樣形成並查集。
寫的時候開陣列為2*n.
接著,利用貪心的思想如果一組資料的兩個犯人已經在一個集合中了,那麼以後就不用再操作了,直接把兩個犯人的怨氣輸出。(公務繁忙的Z市長只會去看列表中的第一個事件
具體方法如圖所示:
程式碼:
#include <algorithm>
#include <iostream>
using namespace std;
int n,m,f[40001],x,y;
struct data
{
int a,b,c;
}e[100001];
int gz(const data &a,const data &b)
{
if(a.c>b.c)return 1;
else return 0;
}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].a>>e[i].b>>e[i].c;
for(int i=1;i<=n*2;i++)
f[i]=i;//自己是自己的父親
sort(e+1,e+m+1,gz);
for(int i=1;i<=m;i++)
{
x=find(e[i].a);
y=find(e[i].b);
if(x==y) //如果兩個犯人在同一監獄了,結束
{
cout<<e[i].c;
return 0;
}
f[y] = find(e[i].a + n);//否則的話,我們把一個犯人的補集和另一個犯人放在一起
f[x] = find(e[i].b + n);//同理
}
cout<<0;
return 0;
}