代码随想录训练营 Day59打卡 图论part09 Bellman_ford算法

代码随想录训练营 Day59打卡 图论part09

Bellman_ford 算法

例题:卡码94. 城市间货物运输 I

题目描述

某国为促进城市间经济交流,决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市,通过道路网络连接,网络中的道路仅允许从某个城市单向通行到另一个城市,不能反向通行。

网络中的道路都有各自的运输成本和政府补贴,道路的权值计算方式为:运输成本 - 政府补贴 。权值为正表示扣除了政府补贴后运输货物仍需支付的费用;权值为负则表示政府的补贴超过了支出的运输成本,实际表现为运输过程中还能赚取一定的收益。

请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。如果最低运输成本是一个负数,它表示在遵循最优路径的情况下,运输过程中反而能够实现盈利。
城市 1 到城市 n 之间可能会出现没有路径的情况,同时保证道路网络中不存在任何负权回路。
输入描述

第一行包含两个正整数,第一个正整数 n 表示该国一共有 n 个城市,第二个整数 m 表示这些城市中共有 m 条道路。

接下来为 m 行,每行包括三个整数,s、t 和 v,表示 s 号城市运输货物到达 t 号城市,道路权值为 v (单向图)。
输出描述

如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市 n,请输出 "unconnected"。
输入示例

6 7

5 6 -2

1 2 1

5 3 1

2 5 2

2 4 -3

4 6 4

1 3 5
输出示例

1
提示信息

示例中最佳路径是从 1 -> 2 -> 5 -> 6,路上的权值分别为 1 2 -2,最终的最低运输成本为 1 + 2 + (-2) = 1。

示例 2:

4 2

1 2 -1

3 4 -1

在此示例中,无法找到一条路径从 1 通往 4,所以此时应该输出 "unconnected"。

本题是求解带有负权边的单源最短路径问题,经典的 Dijkstra 算法要求边权为非负数,因此不能直接使用。为了解决含负权边的最短路径问题,Bellman-Ford 算法是一个很好的选择。

Bellman-Ford 算法简介:

Bellman-Ford 算法适用于求解包含负权边的单源最短路径问题。其主要特点是:

  1. 松弛操作:对每条边执行松弛操作,通过逐步更新节点的最短路径,找到最短路径。
  2. 松弛 n-1 次:在一个有 n 个节点的图中,最短路径最多经过 n-1 条边,因此我们需要对所有边进行 n-1 次松弛操作。
  3. 检测负权环:如果在 n-1 次松弛操作后仍然能继续松弛,说明图中存在负权环。

松弛操作的解释:

假设一条边从 from 节点指向 to 节点,边权为 price,当前 minDistfrom 表示从起点到 from 节点的最短距离。如果 minDistfrom + price < minDistto,说明通过 from 到达 to 更短,因此更新 minDistto

为何松弛 n-1 次:

在一个无环图中,最短路径最多经过 n-1 条边。每次松弛操作会传播最短路径的信息,因此在 n-1 次松弛后,所有节点的最短路径都会正确。如果还有负权环,第 n 次松弛会继续更新,说明存在负权环。

代码实现

python 复制代码
import sys

def bellman_ford(n, m, edges, start, end):
    # 初始化最短距离数组,起点到其他节点的距离初始化为无穷大
    minDist = [float('inf')] * (n + 1)
    minDist[start] = 0  # 起点到自身的距离为 0

    # 对所有边进行 n-1 次松弛操作
    for _ in range(n - 1):
        for from_node, to_node, weight in edges:
            # 如果起点的距离不是无穷大,并且可以通过该边得到更短的距离,进行松弛
            if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + weight:
                minDist[to_node] = minDist[from_node] + weight

    # 检查是否存在负权环:如果在 n-1 次松弛之后还能进行松弛,说明存在负权环
    for from_node, to_node, weight in edges:
        if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + weight:
            print("Graph contains a negative weight cycle")
            return None  # 负权环的存在意味着无法找到最短路径

    # 返回从起点到终点的最短路径
    return -1 if minDist[end] == float('inf') else minDist[end]

# 读取输入并调用 Bellman-Ford 算法
if __name__ == "__main__":
    input = sys.stdin.read
    data = input().split()

    # 读取节点数 n 和边数 m
    n, m = int(data[0]), int(data[1])
    
    # 读取所有边,边的格式为 (起点, 终点, 权值)
    edges = []
    index = 2
    for _ in range(m):
        p1 = int(data[index])  # 起点
        p2 = int(data[index + 1])  # 终点
        val = int(data[index + 2])  # 权值
        edges.append((p1, p2, val))
        index += 3

    start = 1  # 起点
    end = n    # 终点

    # 调用 Bellman-Ford 算法计算从起点到终点的最短路径
    result = bellman_ford(n, m, edges, start, end)
    
    # 输出最短路径结果
    if result is not None:
        print(result)

时间复杂度分析:

时间复杂度:O(n * m),其中 n 是节点数,m 是边数。因为 Bellman-Ford 算法需要对所有边执行 n-1 次松弛操作。

空间复杂度:O(n),用于存储最短路径的数组 minDist。

Bellman-Ford 算法能够有效处理带有负权边的最短路径问题,并且可以检测负权环。它比 Dijkstra 算法更通用,但时间复杂度较高,适用于边权可能为负的情况。

卡码题目链接
题目文章讲解

相关推荐
旋生万物14 分钟前
素数都排在等角螺线上?用螺旋数论给黎曼猜想画一张“几何画像“(附Python)
python·ai编程·数论·黎曼猜想·素数分布
彧azz27 分钟前
二叉搜索树学习记录
c语言·数据结构·笔记·算法
用户37215742613528 分钟前
使用 Python 压缩 PDF 文件:减小图片、字体和文档内容体积
python
java1234_小锋32 分钟前
Tornado 6.5,全面支持 Python 3.14
数据库·python·tornado
IT毕设梦工厂1 小时前
计算机毕业设计选题推荐:基于大数据的印度上市公司财务指标数据可视化分析|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hadoop·python·信息可视化·spark·课程设计·大数据毕设项目
SEO_juper1 小时前
你的服务器正在被 AI 爬虫“白嫖“带宽:2026 用日志把 Googlebot 和 AI 洪流分开算账(附脚本)
运维·人工智能·爬虫·python·chatgpt·seo
浩风祭月1 小时前
GPT Image 2.5 Sunburst还是Flare?模型选型、Python接入与成本避坑
python·aigc·openai·图像生成·gpt image 2.5
手写码匠1 小时前
DeepSeek 函数调用实战:从零搭建一个会“动手“的 AI 助手
人工智能·深度学习·算法·aigc
泡干脆面就番茄1 小时前
03_LBPH人脸识别算法原理与实战
python·opencv
Yanjun2i1 小时前
Agent学习记录四:多工具时如何处理
人工智能·python·学习·agent