10.5 全源负权Floyd-Warshall算法

  Dijkstra算法不能计算权重含有负数的图,所以需要别的算法。Floyd-Warshall就是这种算法。这个算法需要一个前提,图中没有负数距离的环存在。
  Floyd-Warshall算法的目的是输出所有点之间的最短距离。
  算法用到了动态规划思想,最重要的是中间点概念。中间点就是一个路径去掉首位后剩余的点。另外,使用一个距离数组来保存距离。距离数组是个三维数组。
  第一个维度是指路径用到了哪些点。d[0]是只用到了一个点的距离矩阵。
  这个三维数组的每一项都是一个二维数组,也就是距离矩阵。
  算法第一步是floyd-warshall分解
  这是个求动态规范子问题的过程。
  定义 D i j ( k ) D_{ij}^{(k)} Dij(k)是i到j的最短路径,并且所有中间节点都在1~k的集合中。
  动态规划递归的思路是
  1 如果最短路径上没有k
  那么最短路径就是 D i j k − 1 D_{ij}^{k-1} Dijk1
  2 如果最短路径上有k。
  那么最短路径就是 D i k ( k − 1 ) + D k j ( k − 1 ) D_{ik}^{(k-1)}+ D_{kj}^{(k-1)} Dik(k1)+Dkj(k1)
  所以递归步骤就是:
D i j ( k ) = m i n { D i j ( k − 1 ) , D i k ( k − 1 ) + D k j ( k − 1 ) } D_{ij}^{(k)}=min\{D_{ij}^{(k-1)},D_{ik}^{(k-1)}+D_{kj}^{(k-1)}\} Dij(k)=min{Dij(k1),Dik(k1)+Dkj(k1)}
  对于这个三维数组,就只能才用自底部向上的办法了。
  算法步骤如下:
  第一步 初始化k为0的矩阵
D i j 0 = W i j D_{ij}^0=W_{ij} Dij0=Wij
  因为递归是按k进行递归的
  所以依次计算 D 1 D^1 D1, D 2 D^2 D2, D 3 D^3 D3,一直到 D n D^n Dn矩阵。
  需要注意的是 D 1 D^1 D1是相当于编程中的数组的0索引,而 D 0 D^0 D0是最早初始化的。所以D这个数组的长度其实是n+1。
  用这个图测试
在这里插入图片描述

  以S为例子
  S到B的距离,最短路径是2,路径是SCAB这个路径.
  S索引为3,c索引为2,a索引为0,b索引为1。
  所以d[3][3][1]=2,代码如下:

def floyd_warshall(self):
    d = [[[0 if i == j else float('inf') for j in self.__vertices] for i in self.__vertices]]
    # 先初始化距离
    for e in self.__edges:
        d[0][e.from_index][e.to_index] = e.weight
    n = len(self.__vertices)

    for k in range(1, n + 1):
        d.append([[float('inf') for _ in self.__vertices] for _ in self.__vertices])
        for i in range(0, n):
            for j in range(0, n):
                d[k][i][j] = min(d[k - 1][i][j], d[k - 1][i][k - 1] + d[k - 1][k - 1][j])
    return d[n]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值