📜  门|门 CS 1997 |第 48 题(1)

📅  最后修改于: 2023-12-03 14:58:35.001000             🧑  作者: Mango

门|门 CS 1997 |第 48 题

题目描述

给定一张 $n$ 个点 $m$ 条边的无向图。定义一条路径 $P$ 的“质量”定义为 $P$ 上边权的最大值。求出从 $s$ 到 $t$ 的所有路径中质量最小的路径的质量。

输入格式

第一行两个整数 $n,m$。

接下来 $m$ 行,每行三个整数 $x,y,z$,表示点 $x,y$ 之间有一条边,边权为 $z$。

最后一行两个整数 $s,t$。

输出格式

一个实数,四舍五入保留 $2$ 位小数。

数据范围

$1\leq n\leq 500$,$1\leq m\leq 10^4$。

示例

输入:

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

输出:

3.00
算法1

(二分答案 + DFS) $O(mlog_2w+n)$

由于质量单调不降,所以我们可以对答案进行二分。判断质量为 $mid$ 的路径是否存在,可以采用 DFS。

DFS

对于本题,DFS 的实现步骤为:

  1. 使用 visited 数组标记访问过的节点,防止重复访问;
  2. 将起点入队,并设置 visited[s] 为真;
  3. 从队列中弹出节点 x,遍历它的所有未访问的邻居节点,如果这些邻居使得路径上的边权最大值大于等于 mid,就将它们入队,并设置 visited[x] 为真;
  4. 直到队列为空或者到达终点,停止遍历。
代码
def dfs(x, mid):
    queue = [x]
    visited[x] = True
    while queue:
        node = queue.pop(0)
        if node == t:
            return True
        for neighbor, weight in graph[node]:
            if not visited[neighbor] and weight >= mid:
                visited[neighbor] = True
                queue.append(neighbor)
    return False

l, r = 0, max_weight
while l < r:
    mid = (l + r + 1) // 2
    visited = [False] * (n + 1)
    if dfs(s, mid):
        l = mid
    else:
        r = mid - 1
print('{:.2f}'.format(l))
算法2

(迪杰斯特拉) $O(mlog_2w+n^2)$

本题还可以使用迪杰斯特拉算法求解。将边权当做距离,使用迪杰斯特拉算法求出 $s$ 到其他点的最短距离 $d_i$。最后,从 $s$ 到 $t$ 的所有路径中,选取最小的 $max_{i\in P} d_i$ 即可。

值得注意的是,迪杰斯特拉算法不能处理边权为负的情况。但是本题的边权是正数,所以可以使用迪杰斯特拉算法求解。

代码
import heapq

INF = 10**6

d = [INF] * (n + 1)
d[s] = 0
heap = [(0, s)]
while heap:
    dist, node = heapq.heappop(heap)
    if node == t:
        break
    if d[node] < dist:
        continue
    for neighbor, weight in graph[node]:
        new_dist = max(dist, weight)
        if new_dist < d[neighbor]:
            d[neighbor] = new_dist
            heapq.heappush(heap, (new_dist, neighbor))
print('{:.2f}'.format(d[t]))
算法3

(最短路,把边权取反后求最短路) $O(mlog_2w+n^2)$

由于边权都是正数,因此我们可以采用一种奇技淫巧:把边权取反后再求最短路。

这种方式的思想是,最短路径上边权最小,所以把路径中的所有边权取反后,原来最小的路径就变成了最大的路径,原来次小的路径就变成了次大的路径。

代码
def dijkstra(s):
    d = [INF] * (n + 1)
    d[s] = 0
    heap = [(0, s)]
    while heap:
        dist, node = heapq.heappop(heap)
        if d[node] < dist:
            continue
        for neighbor, weight in graph[node]:
            new_dist = -1 * min(dist, -1 * weight)
            if new_dist < d[neighbor]:
                d[neighbor] = new_dist
                heapq.heappush(heap, (new_dist, neighbor))
    return d

d1 = dijkstra(s)
print('{:.2f}'.format(-1 * d1[t]))
算法4

(SPFA) $O(km+n^2)$

SPFA 方法也可以用于解决此问题。首先,给所有的边权值取反,然后进行 SPFA 求出 $s$ 到 $t$ 的最短路径,最终再将答案求反输出即可。

SPFA 这种算法的缺点是,如果图中有负环,它将一直在负环中循环。对于本题,边权都是正数,因此可以使用 SPFA 算法。

代码
def spfa(s):
    dist = [INF] * (n + 1)
    dist[s] = 0
    queue = [s]
    in_queue = [False] * (n + 1)
    in_queue[s] = True
    while queue:
        node = queue.pop(0)
        in_queue[node] = False
        for neighbor, weight in graph[node]:
            if dist[node] + weight < dist[neighbor]:
                dist[neighbor] = dist[node] + weight
                if not in_queue[neighbor]:
                    queue.append(neighbor)
                    in_queue[neighbor] = True
    return dist

d1 = spfa(s)
print('{:.2f}'.format(-1 * d1[t]))