0
点赞
收藏
分享

微信扫一扫

13.5 加权图Floyd-Warshall算法

color_小浣熊 2022-03-30 阅读 64

  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的距离,最短路径是0,路径是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]
举报

相关推荐

0 条评论