P1525 [NOIP 2010 提高组] 关押罪犯 题解
这是我第一次解决非模版并查集问题
我的思考
本来看到最大值最小,就想到二分,but,如何二分?其实也有这种方法,不过我不会
这是并查集题单里的题目,但是根本想不出来如何用并查集解决问题,因为有仇恨值
于是我去看了题解(没思考多久,主要是真的不会并查集活用)
不得不说一开始,我没看到只有两个监狱这个条件,看到题第一时间认真审题,审题,审题!
解法
每一对罪犯,都有对应的仇恨值,我们做的就是要让大的取不到
于是,这就有了第一个要点,我们预处理这些数据,将他们从大到小排序,然后依次处理。
如何处理?
敌人的敌人,就是朋友(同一监狱),比如比如先看到了A、B,然后看到了B、C,A,C就是一个监狱的
什么时候结束程序?如果两位冤家,已经在同一监狱,自然程序就结束了
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 2e5 + 2;
int id[N], b[N];
struct D {
int u, v, w; // 这些就是输入的东西
} a[N];
bool cmp(D x, D y) {
return x.w > y.w;
}
inline int find(int now) {
if (id[now] == now) {
return now;
} else {
id[now] = find(id[now]);
return id[now];
}
} // 纯板子
inline void merge(int x, int y) {
int px = find(x), py = find(y);
if (px == py) return;
id[px] = py;
} // 纯板子
inline bool check(int x, int y) {
if (find(x) == find(y))
return true;
else
return false;
} //判断是不是已经在一个监狱了
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
id[i] = i;
} // 一定不要忘记写,不然...(经常不写)
for (int i = 1; i <= m; i++) {
cin >> a[i].u >> a[i].v >> a[i].w;
}
sort(a + 1, a + m + 1, cmp); // 从大到小排序
for (int i = 1; i <= m; i++) {
if (check(a[i].u, a[i].v)) { // 是不是已经在同一监狱了
cout << a[i].w;
return 0;
} else {
// b就是统计一个罪犯的敌人是谁的
if (!b[a[i].u]) { // 原本没有敌人
b[a[i].u] = a[i].v; // 现在有了
} else {
merge(b[a[i].u], a[i].v); // 敌人的敌人就是朋友
}
if (!b[a[i].v]) { // 同理
b[a[i].v] = a[i].u;
} else {
merge(b[a[i].v], a[i].u);
}
}
}
cout << 0; // 保底
return 0;
}
总结
监狱数量是固定的,有的时候你可以在它不变的数量上做文章,比如2就是非我即它或敌人的敌人就是朋友(当然,不止这些)。
这不代表最大值最小问题不能二分了,但是也不是一定要用二分,比如这里就是用了排序和并查集(负责把“敌人的敌人”拉进同一个监狱)。

浙公网安备 33010602011771号