P1525 [NOIP2010 提高组] 关押罪犯(种类并查集基础)(详解)

题目链接:关押罪犯

题目描述

一共有 n 名罪犯,m 对罪犯有矛盾,矛盾值为 c ,有两座监狱。关键点在于:敌人的敌人是朋友。 现 Z 市长要看到其中最大的矛盾值。问:应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入格式

每行中两个数之间用一个空格隔开。第一行为两个正整数 N,MN,M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 MM 行每行为三个正整数 aj,bj,cj,表示 aj 号和 bj 号罪犯之间存在仇恨,其怨气值为 cj 。数据保证 1 < aj ≤ bj ≤ N,0 < cj ≤ 109,且每对罪犯组合只出现一次。

输出格式

共 1 行,为 Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 0。

输入输出样例

输入 #1

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

输出 #1

3512

说明/提示

题目分析

Z 市长要看到的是最终最大的矛盾值,所以我们需将数据按矛盾值从大到小的顺序进行排序,然后尽可能将矛盾值大两个罪犯的分配到不同的监狱。因为要分配到两个监狱,我们自然会想到运用种类并查集来做。

因为由 2 座监狱,所以把并查集分为 2 块,把数组扩大为 2*n,两段并查集分别代表两座监狱。
merge(x, y+n);
merge(y, x+n);

注释:敌人的敌人是朋友

AC代码

(函数中没注释的地方,详解见)

#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
int f[maxn], h[maxn], n, m, x, y, c;
//用结构体储存数据 
struct node {
    
    
	int x, y, c;
} a[maxn];
bool cmp(node a, node b) {
    
    
	return a.c > b.c;
	//以矛盾值作为判断依据,为下面的排序做准备 
}
void Init() {
    
    
	for(int i=1; i<=2*n; i++) {
    
    
		f[i] = i;
		h[i] = 0;
	}
}
int Find(int i) {
    
    
	return f[i]==i ? f[i] : f[i]=Find(f[i]);
}
void merge(int a, int b) {
    
    
	int fa = Find(a);
	int fb = Find(b);
	if(fa != fb) {
    
    
		if(h[fa] < h[fb]) {
    
    
			f[fa] = fb;
		} else {
    
    
			f[fb] = fa;
			if(h[fa]==h[fb]) h[fa]++;
		}
	}
}

int main(void) {
    
    
	scanf("%d %d", &n, &m);
	Init();   //初始化 
	for(int i=1; i<=m; i++) 
		scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].c);
	sort(a+1, a+1+m, cmp);  //将数据按照矛盾值从大到小排序 
	for(int i=1; i<=m; i++) {
    
    
		int x = a[i].x;
		int y = a[i].y;
		if(Find(x) == Find(y)) {
    
    
		//如果 x和 y在同一座监狱,证明无法再继续优化分配了
		//因为前面矛盾值大的都已经优化分配了
		//那么此时这对的矛盾值就是 Z市长看到的最大矛盾值
		//直接输出并结束 
			printf("%d\n", a[i].c);
			return 0;
		}
		//敌人的敌人是朋友 
		merge(x, y+n);
		merge(y, x+n);
	}
	printf("0\n"); //特殊情况:没有任何冲突事件 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51250927/article/details/113619285