【题解】洛谷P1396 营救

【题解】洛谷P1396 营救

题目传送门

一道并查集+二分的题。(刷水题找信心

看到最大的最小就想到二分。那就稍微看看单调性:

易得存在一个满足题意的拥挤度 \(w\) ,并且使得 \(w-1\) 不满足题意,而 \(w+1\) 满足题意。稍微想想可知此题满足二分的单调性,就可以二分 \(w\) 来求解。

考虑二分的检查函数,题目只要求我们判断图上两个节点是否连通,那么用并查集维护即可。

代码:

#include<bits/stdc++.h>

const int maxn = 2e4 + 10;

struct node // 存储边的结构体
{
    int x, y, w;
} a[maxn];

int father[maxn], n, m, s, t;

// 并查集模板
void init(int size) // 初始化
{
    for (int i = 0; i <= size; i++)
        father[i] = i;
}

int find(int x) // 找祖先
{
    if (father[x] != x)
        father[x] = find(father[x]);
    return father[x];
}

void uni(int x, int y) // 合并
{
    x = find(x);
    y = find(y);
    father[x] = y;
}

bool check() { return find(s) == find(t); } // 找是否有公共祖先

bool solve(int x) // 二分的判断函数
{
    init(m);
    for (int i = 0; i < m; i++)
        if (a[i].w <= x)
            uni(a[i].x, a[i].y); // 不超过 x 的连起来
    if (check())
        return true;
    else
        return false;
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for (int i = 0; i < m; i++)
        scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].w);
    int l = 1, r = -10;
    for (int i = 0; i < m; i++)
        r = std::max(r, a[i].w); // 找二分边界:最大的 w
    while (l <= r) // 我习惯写闭区间的二分
    {
        int mid = (l + r) / 2;
        if (solve(mid)) // mid 满足要求说明 w 还可以更小
            r = mid - 1;
        else
            l = mid + 1;
    }
    if (solve(r)) // 结束后 r 更小,这么写省点麻烦
        printf("%d\n", r);
    else
        printf("%d\n", l);
    return 0;
}
posted @ 2021-03-13 18:02  _slb  阅读(82)  评论(0编辑  收藏  举报