[luogu p1525] 关押罪犯
关押罪犯
题目描述
S 城现有两座监狱,一共关押着 \(N\) 名罪犯,编号分别为 \(1-N\)。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用"怨气值"(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 \(c\) 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 \(c\) 的冲突事件。 、
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了\(N\) 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。
那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入输出格式
输入格式
每行中两个数之间用一个空格隔开。第一行为两个正整数 \(N,M\),分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 \(M\) 行每行为三个正整数 \(a_j,b_j,c_j\),表示 \(a_j\) 号和 \(b_j\) 号罪犯之间存在仇恨,其怨气值为 \(c_j\)。数据保证 \(1<a_j\le b_j \le N, 0 < c_j \le 10^9\),且每对罪犯组合只出现一次。
输出格式
共 \(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
说明
【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是 \(3512\)(由 \(2\) 号和 \(3\) 号罪犯引发)。其他任何分法都不会比这个分法更优。
【数据范围】
对于 \(30\%\)的数据有 \(N\leq 15\)。
对于 \(70\%\) 的数据有 \(N\leq 2000,M\leq 50000\)。
对于 \(100\%\) 的数据有 \(N\leq 20000,M\leq 100000\)。
分析
此题有两种方法做:并查集和二分图做法。此处并不讨论二分图做法,那么蟹蟹为什么要打上二分图的tag呢?别问我我也不知道
二分图做法好想但不是很好写,并查集做法好写但不是很好想。但因为蟹蟹懒,所以咱们这里就说并查集做法了。
我们用两个集合表示两个监狱,用并查集来维护。首先,我们先输入所有的仇恨关系,然后按照仇恨关系按照仇恨值从大到小依次排列,分别处理。
对于每一次仇恨,我们进行以下处理:
- 首先,如果两个有仇恨关系的人在同一个集合,那么输出仇恨值,结束程序。因为我们已经按照仇恨值从大到小进行排列了,那么显然此时的仇恨值就是最大仇恨值,直接结束程序。
- 否则,我们将其中一个仇人的仇人和他放在一个集合。(此处为算法的核心),比如1号和2号是仇人,2号和3号是仇人,那么就把1和3放在一个集合。因为已经按照从大到小排列了,说明1和3就算有仇恨关系也比1和2,2和3的仇恨关系小(要不然就优先处理了),此时是最优的选择。
最后别忘了,如果可以保证没有任何一对有仇的人在同一个监狱,要输出0.
上代码:
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-08-18 19:27:22
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-08-19 01:17:24
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
const int maxn = 20005;
const int maxm = 100005;
struct hatred {
int a, b;
int num;
}a[maxm];
int fa[maxn], enemy[maxn];//enemy用来记录仇人
bool cmp(const hatred a, const hatred b) {
return a.num > b.num;
}
int find(int x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
void unite(int x, int y) {
int fax = find(x);
int fay = find(y);
fa[fax] = fay;
}
bool check(int x, int y) {
int fax = find(x);
int fay = find(y);
return fax == fay;
}
int main() {
int n, m;
std :: scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
fa[i] = i;
for (int i = 1; i <= m; ++i)
std :: scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].num);
std :: sort(a + 1, a + 1 + m, cmp);
bool flag = false;
for (int i = 1; i <= m; ++i) {
if (check(a[i].a, a[i].b)) {
std :: printf("%d\n", a[i].num);
flag = true;
break;
}
if (!enemy[a[i].b])
enemy[a[i].b] = a[i].a;//记录b的仇人
else
unite(a[i].a, enemy[a[i].b]);//将a和b的仇人合并
if (!enemy[a[i].a])
enemy[a[i].a] = a[i].b;//记录a的仇人
else
unite(a[i].b, enemy[a[i].a]);//将b和a的仇人合并
}
if (!flag)
std :: printf("0\n");//如果没有flag,输出0
return 0;
}