# AcWing 851. spfa 求最短路

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。

数据保证不存在负权回路。

输入格式
第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 impossible。

数据范围

1≤n,m≤105,
图中涉及边长绝对值均不超过 10000。

输入样例

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

输出样例

2

# 题目描述

给定一个有 n 个点和 m 条边的有向图,图中可能存在重边和自环,并且存在负权边。问题是让我们求出从 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,输出 - 1。

# bellman-ford 的思路及核心代码回顾

spfa 本质上是对 bellman-ford 的一个优化,因此我们先回顾一下 bellman-ford 的思路及实现。

在 bellman-ford 中,使用的方式是每次把所有的边与目标点的距离都更新一步。用代码表示即为:

for i in range(n):
    back = deepcopy(dist)
    for a, b, w in g[i]:
        dist[b] = min(dist[b],back[a] + w)

其中几个变量的定义如下:

  1. g 为图本身。g [i] 表示的是 a 到 b 的距离是 w;

  2. dist 数组表示的是其中元素到目标点的距离;

  3. back 数组表示的是 dist 数组的备份,既最外层循环的上一轮循环中的 dist;

我们注意看代码中点 b 的更新。其中 dist [b] 表示此时 b 到目标点的距离,而在本次循环中,a 与 b 之间有一条边,权重是 w,那么如果目标点到 a 的距离,加上 a 到 b 的距离,小于目标点直接到 b 的话,说明找到了更短的一条路径(既先到 a 再到 b),所以可以用 (back [a]+w) 更新 dist [b]。

那么这样做有什么问题呢?显而易见的,如果 b 到 a 再到目标点的距离没有 b 直接到目标点更短,则本次的遍历就是无效的,也就是说 dist [b] 不会被更新,而 spfa 正是优化了这种无效的遍历。(这段话需要认真体会)

# Code

从代码中我们可以看出来核心思路是完全没有变化的。既代码中的注释部分:

如果目标点到当前点再到 b 小于 目标点直接到 b 则更新。

时间复杂度上,我们只遍历到目标点距离变小的点,所以时间复杂度平均为 O (m),而最坏则还是会达到 bellman-ford 的时间复杂 u 度 O (nm)。

from collections import defaultdict, deque
# 读入数据
n, m = list(map(int, input().split()))
g = defaultdict(lambda: defaultdict(lambda: int))
for _ in range(m):
    a, b, w = list(map(int, input().split()))
    g[a][b] = w
# 初始化距离
dist = [float('inf')] * (n + 10)
dist[1] = 0
# 判重数组,如果需要判断的点已经在队列,则不需要再加入
visited = [False] * (n + 10)
visited[1] = True
# 使用队列来存储,变小的点,初始化为目标点
q = deque()
q.append(1)
while q:
    # 弹出当前点  当前点不在队列所以判重数组置为 False
    cur = q.popleft()
    visited[cur] = False
    # 遍历当前点可以到达的点 b
    for b in g[cur]:
        # 如果目标点到当前点再到 b 小于 目标点直接到 b 则更新
        if dist[b] > g[cur][b] + dist[cur]:
            dist[b] = g[cur][b] + dist[cur]
            # 因为 b 是被更新过的点  所以可以更新它的可到达的点
            if not visited[b]:
                q.append(b)
                visited[b] = True
print(dist[n] if dist[n] != float('inf') else 'impossible')
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

A Cat Without Sugar 微信支付

微信支付

A Cat Without Sugar 支付宝

支付宝