题目链接:关押罪犯
题目描述
一共有 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;
}