【並查集】關押罪犯
題目描述
S城現有兩座監獄,一共關押著N名罪犯,編號分別為1~N。他們之間的關係自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。我們用“怨氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為c的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為c的衝突事件。每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成一個列表,然後上報到S城Z市長那裡。公務繁忙的Z市長只會去看列表中的第一個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。
在詳細考察了N名罪犯間的矛盾關係後,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。那麼,應如何分配罪犯,才能使Z市長看到的那個衝突事件的影響力最小?這個最小值是多少?
輸入
輸入每行中兩個數之間用一個空格隔開。第一行為兩個正整數N和M(N≤20000,M≤100000),分別表示罪犯的數目以及存在仇恨的罪犯對數。
接下來的M行每行為三個正整數aj,bj,cj,表示aj號和bj號罪犯之間存在仇恨,其怨氣值為cj。資料保證1≤aj<bj≤N,0<cj≤1,000,000,000,且每對罪犯組合只出現一次。
輸出
輸出共1行,為Z市長看到的那個衝突事件的影響力。如果本年內監獄中未發生任何衝突事件,請輸出0。
樣例輸入
4 6 1 4 2534 2 3 3512 1 2 28351 1 3 6618 2 4 1805 3 4 12884
樣例輸出
3512
提示
罪犯之間的怨氣值如下面左圖所示,右圖所示為罪犯的分配方法,市長看到的衝突事件影響力是 3512(由 2 號和 3 號罪犯引發)。其他任何分法都不會比這個分法更優。
參考:https://blog.csdn.net/yznoi2017/article/details/52841552
很容易想到的貪心,kruskal先將邊權大的放在不同監獄,那麼這裡並查集就是維護u與v不在同一個監獄,然而問題就來了,假設E1{a,b},E2{a,c},第一次操作fa[a]=b即a與b不在同一監獄,到第二次操作時fa[a]=fa[b]=c,那麼就表示a與c不在同一監獄,b與c不在同一監獄,顯然與上面a與不在同一監獄。我們可以將a’,b’,c’分別與a,b,c對應,f[a]=b’表示a與b’不在同一監獄,然而這裡b’只是一個虛點,所以它不會影響之後的操作。
先將邊按照由大到小排序 kruskal將邊權大的放在不同的監獄,用並查集維護u與v不在同一個監獄。(如果a和b在同一個並查集內 則表示a與b不同監獄)
然而問題就來了,假設E1{a,b},E2{a,c},第一次操作fa[a]=b即a與b不在同一監獄,到第二次操作時fa[a]=fa[b]=c,那麼就表示a與c不在同一監獄,b與c不在同一監獄,顯然與上面a與不在同一監獄。我們可以將a’,b’,c’分別與a,b,c對應,f[a]=b’表示a與b’不在同一監獄,然而這裡b’只是一個虛點,所以它不會影響之後的操作。
例如樣例中排序後的邊為1 2;3 4;1 3;2 3;1 4;2 4
則第一步: 1->6,1->5
第二步:3->8, 4->7
第三步:1->7, 3->5
第四步:2->7,3->6 連完後發現有迴路 則這個矛盾是無法避免的最大矛盾 輸出
程式碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20005;
const int maxm = 100005;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct node{ int u,v,w; bool operator< (const node &x) const { return w>x.w; } }a[maxm]; int fa[maxn*2]; int find(int x) { return x==fa[x]?x:fa[x]=find(fa[x]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){a[i].u=read();a[i].v=read();a[i].w=read();} sort(a+1,a+1+m); // for(int i=1;i<=m;i++) // printf("%d %d %d\n",a[i].u,a[i].v,a[i].w); for(int i=1;i<=n*2;i++) fa[i] = i; for(int i=1;i<=m;i++) { int uu = a[i].u,vv=a[i].v,ww=a[i].w; int fa1 = find(uu); int fa2 = find(vv); if(fa1==fa2) { printf("%d",ww); return 0; } fa[fa1] = find(vv+n); fa[fa2] = find(uu+n); // printf("%d %d\n",fa1,fa[fa1]); // printf("%d %d\n\n",fa2,fa[fa2]); } printf("0"); return 0; }