1. 程式人生 > >並查集 —— P1525 關押罪犯

並查集 —— P1525 關押罪犯

題目描述

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

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

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

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

題目地址:https://www.luogu.org/problemnew/show/P1525

 

思路:

  • 首先,因為要保證最大的衝突事件影響力最小,所以我們可以想到貪心+遞推的思想,希望能保證兩個監獄的目前最大影響值最小
  • 貪心的話,我們可以首先讀取所有邊(把兩個罪犯看作兩個點,他們之間的仇恨值看作一條邊),並將他們由大到小排序,並使得一條邊上的兩個罪犯儘量不在同一個監獄。因為我們是貪心,所以可以證明:如果兩個罪犯不得不進入一個監獄,那麼由於當前已經進入監獄的罪犯們之間的仇恨值必定比當前仇恨值大,所以他們之間的這個仇恨值就是最大仇恨值。
  • 但是這樣就有了一個問題:如何使得仇恨值大的罪犯們不在同一個監獄?首先,我們可以想到做兩個集合,並使得一條邊上的一個點放到集合A,另一個點放到集合B。但是,僅僅這樣顯然是不行的。這是因為放入集合A的那個點可能會與集合A中的某個點有仇恨,這就不滿足遞推的條件了(我們遞推的條件是當前情況已經是最優)
  • 那麼,我們就要保證這個即將放入某個集合的點和這個集合的其他點沒有仇恨。但這是不能保證的,這個之後討論。假設給的資料保證最後的最優解必定為0,也就是說每個監獄最終的總仇恨值都為0。
  • 那麼這時候,我們在遍歷邊的時候,就可以不直接吧兩個點分別加入兩個監獄集合,而是首先儲存兩個點之間是仇人關係,如果兩個點已經是仇人關係,就讓他們分別進入兩個監獄。由於遞推和之前的條件,這樣是沒問題的。(這個假設不理解也沒關係,因為我自己都不太理解
  • 接下來可以看這篇題解:https://www.luogu.org/blog/tanktt/solution-p1525 然後大概知道是怎麼回事之後畫一遍樣例即可。

 

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int cnt=0;
int n,m,fa[40005],b[40005];
struct Edge{
	int u,v,w;
}e[100005];
int cmp(Edge a,Edge b){
	return a.w>b.w;
}
void addEdge(int u,int v,int w){
	e[++cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
}
int find(int a){
	while(a!=fa[a]){
		a=fa[a]=fa[fa[a]];
	}
	return a;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		addEdge(a,b,c);
	}
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	sort(e+1,e+m+1,cmp);
	int ans=0;
	for(int i=1;i<=m+1;i++){
		int eu=e[i].u,ev=e[i].v;
		//cout<<eu<<" "<<ev<<endl;
		if(find(eu)==find(ev)){
			printf("%d\n",e[i].w);
			return 0;
		}
		if(!b[eu])b[eu]=ev;//如果點eu沒有敵人的話,這次迴圈就只設置一下敵人
		else fa[find(b[eu])]=find(ev);//如果已經有敵人了,就將自己敵人放到和自己不同的陣營
		if(!b[ev])b[ev]=eu;
		else fa[find(b[ev])]=find(eu);
	}
	printf("0\n");
	return 0;
}

 


歡迎加入我們的OI討論群

群號:849352599