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就是非我即它敌人的敌人就是朋友(当然,不止这些)。
不代表最大值最小问题不能二分了,但是也不是一定要用二分,比如这里就是用了排序并查集(负责把“敌人的敌人”拉进同一个监狱)。

posted @ 2026-04-26 17:59  PCMSFV  阅读(3)  评论(1)    收藏  举报