P1525 [NOIP2010 提高组] 关押罪犯

带权并查集中,dist[]数组可以理解为一个向量,这样子比按照距离来理解更透彻:

优秀学习资料:

AcWing 240. 食物链(带权并查集) - AcWing

即d[a]表示向量a->fa[a]

这道题的并查集解法:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <cmath>
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;

const int N = 20005, M = 100005;

int fa[N], d[N];
int n, m;
struct _edges
{
    int a, b, w;
    bool operator<(const _edges &t) const
    {
        return w > t.w;
    }
} Edge[M];

void init(int n)
{
    for (int i = 1; i <= n; i++)
        fa[i] = i;
}

int find(int a)
{
    if (a == fa[a])
        return a;
    int old_fa = fa[a];
    fa[a] = find(old_fa);
    d[a] = (d[a] + d[old_fa]) % 2;
    return fa[a];
}

int solve()
{
    for (int i = 1; i <= m + 1; i++)
    {
        int a = Edge[i].a, b = Edge[i].b;
        int x = find(a), y = find(b);
        if (x != y)
        {
            d[x] = (d[b] - d[a] + 3) % 2;
            fa[x] = y;
           // printf("D %d %d %d %d %d\n", a, b, x, y, d[x])
        }
        else
        {
            if((d[a] - d[b] + 2) % 2 == 0)
                return Edge[i].w;
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d", &n, &m);
    init(n);
    int a, b, w;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d%d", &a, &b, &w);
        Edge[i] = {a, b, w};
    }
    Edge[m + 1] = {0, 0, 0};
    sort(Edge + 1, Edge + m + 1);
    cout << solve();
    return 0;
}

一开始把贪心想错了,把所有节点联通之后才开始计算答案,实际上,在整棵树连通之前,可能就已经有节点通过前面的边间接确定了关系,这时候它们就不得不分配在同一个监狱了,此时已经可以返回答案了

解法二:

二分+二分图,二分一个权值的阈值,超过这个阈值的边,就把它们连起来,如果能够建立二分图(用二分图判定定理判断),就说明这个阈值还能够提高

这里边的实际含义相当于“不能住在同一个监狱”,如果能建立二分图的话,两个集合就分别表示两个监狱。边只存在于两个集合之间,而不存在于集合内部,现实含义就是:

监狱一和监狱二中的罪犯之间都不能住在一起,而集合内部没有边,即同一个监狱的罪犯都住在一起。

 

posted @ 2024-03-25 20:36  Gold_stein  阅读(3)  评论(0编辑  收藏  举报