Floyd

Floyd 算法本质是动态规划

基本要点

  1. 时间复杂度是 O(n^3),因此,只适用于n<500 的这种结点数不多的情况。
  2. 它是多源最短路的,可以求出任何两点之间的最短路。
  3. 支持负边权
  4. 能够判断图中是否存在负回路

代码:

#include<bits/stdc++.h>
using namespace std;
int g[N][N];
int dis[N][N];   // dis[i][j]: i->j 最短距离 

/*
证明:定义 dis[i][j][k] 表示从 i 到 j 的可以经过 1~k 号结点的最短路径,此问题可以被分解为两个子问题
(1)不经过 k 号结点,即  dis[i][j][k-1]
(2)经过 k 号结点,即 dis[i][k][k-1]+dis[k][j][k-1];
因此,有:
dis[i][j][k] = min(dis[i][j][k-1], dis[i][k][k-1]+dis[k][j][k-1]);

上式中第三维 k 可以省略,则有: 
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);

虽然 i,j,k 的顺序不保证,导致上式中 dis[i][k] 和 dis[k][j] 可能是已经被更新过的 dis[i][k][k] 和 dis[k][j][k] 了,
但是这并不影响其正确性。因为更新后的值一定会变得更小,不会影响最终结果。 
*/ 

int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            cin >> g[i][j];
            dis[i][j] = g[i][j];
        }
    // floyd 求任意两点间的最短路径 
    for(int k = 1; k <= n; k++)   // 枚举中间点 
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n;j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
                
    // 判断负环 
    for(int i =1; i <= n; i++)
    {
        if(dis[i][i] < 0) {
            cout<<"存在负环";
            break;
        }
    }       
    return 0;
}

Copy

证明:

定义 dis[i][j][k]表示从 i 到 j的可以经过 1~k 号结点的最短路径,此问题可以被分解为两个子问题

  1. 不经过 k 号结点,即 dis[i][j][k-1]
  2. 经过 k 号结点,即 dis[i][k][k-1]+dis[k][j][k-1]

因此,有:
dis[i][j][k] = min(dis[i][j][k-1], dis[i][k][k-1]+dis[k][j][k-1]);
上式中第三维 k 可以省略,则有:
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
虽然去掉一维后,i,j,k 的顺序不保证,导致上式中 dis[i][k] 和 dis[k][j] 可能是已经被更新过的 dis[i][k][k]和 dis[k][j][k] 了,但是这并不影响其正确性。因为更新后的值一定会变得更小,不会影响最终结果。

Floyd 算法为什么把k放在最外层?

采用动态规划思想,f[k][i][j] 表示 i和 j 之间可以通过编号不超过 kk,即编号 1,2,...,k的节点的最短路径。初值 f[0][i][j]为原图的邻接矩阵。

则该问题可以划分为两个子问题:

  1. f[k][i][j] 可以从 f[k-1][i][j]转移来,表示 i到 j不经过 k 这个节点。

  2. 也可以从 f[k-1][i][k] + f[k-1][k][j]转移过来,表示经过 k 这个点。

于是:f[k][i][j]=min(f[k−1][i][j],f[k−1][i][k]+f[k−1][k][j])

然后你就会发现 f 最外层一维空间可以省略,因为 f[k][][] 只与 f[k-1][][]有关,虽然省略后,无法确定 k,i,j的关系,但是 f[k][i][k]和 f[k][k][j] 是由 f[k-1][i][k]和 f[k-1][k][j] 计算而来,因此前者一定小于等于后者,省略后再取最小值并不会影响最终结果的正确性。

练习题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10247D

我会继续努力,信息技术会越好

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

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

打赏作者

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

抵扣说明:

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

余额充值