📜  门|门CS 2010 |问题 15(1)

📅  最后修改于: 2023-12-03 15:12:47.268000             🧑  作者: Mango

题目描述

给定一个含有 $n$ 个结点的有向图 $G=(V,E)$,每条边 $(u,v)\in E$ 有权值 $w_{u,v}$。图中还有两个不同的结点 $s$ 和 $t$,请你求出从 $s$ 到 $t$ 的一条路径,使得路径上边权的最大值最小,并输出这个最小值。

输入格式

第一行包含三个整数 $n,m,S,T$,分别表示结点数量、有向边数量、起点和终点。

接下来 $m$ 行,每行包含三个整数 $u,v,w$,表示一条有向边 $(u,v)$,边权为 $w$。

输出格式

输出一个整数,表示从 $s$ 到 $t$ 的一条路径,使得路径上边权的最大值最小的最小值。

数据范围

$1\le n,m\le 10^5$, $1\le w_{u,v}\le 10^9$。

示例

输入:

4 5 1 4
1 2 2
2 4 5
2 3 4
3 4 3
1 3 1

输出:

4

算法思路

这是一个最短路径问题,但是根据题意我们需要输出的是从 $s$ 到 $t$ 的一条路径,使得路径上边权的最大值最小。 用二分法寻找我们最终想要的边权 $x$,然后判断是否存在一条从 $s$ 到 $t$ 的路径,满足路径上所有边的边权都不大于 $x$。

如果存在这样的路径,则说明当前的二分边界值太大了,需要继续缩小,比当前二分边界小的值也不满足条件;如果不存在这样的路径,则说明当前的二分边界值太小了,需要继续增大,比当前二分边界大的值均满足条件。

这里我们使用 BFS 算法作为寻找是否存在满足条件的路径的工具,每次 BFS 需要用到队列数据结构。

算法代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10;

int h[N], e[M], w[M], ne[M], idx;
int n, m, S, T;
int q[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

bool bfs(int mid)
{
    int hh = 0, tt = 0;
    memset(st, 0, sizeof st);

    q[0] = S, st[S] = true;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (w[i] >= mid && !st[j])
            {
                st[j] = true;
                q[ ++ tt] = j;
            }
        }
    }
    return st[T];
}

int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m >> S >> T;

    int maxv = 0, minv = 1e9;
    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        maxv = max(maxv, c);
        minv = min(minv, c);
    }

    int l = minv, r = maxv;
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (bfs(mid)) l = mid;
        else r = mid - 1;
    }

    cout << l << endl;
    return 0;
}