当前位置: 首页 > news >正文

Floyd 算法--多源最短路

一、题目描述 

​ 二、解题思路-Floyd算法

(1)最优子结构性质

(2)动规五部曲

三、最终代码

滚动数组优化


一、题目描述 

 二、解题思路-Floyd算法

在这之前我们讲解过,dijkstra、Bellman算法都是单源最短路,即只能有一个起点。

而本题是多源最短路,即 求多个起点到多个终点的多条最短路径。通过本题,我们来系统讲解一个新的最短路算法-Floyd 算法。【当需要计算所有顶点对之间的最短路径时,如果 源点少,其实可以 多次dijsktra 求源点到终点。s

  • 迪杰斯特拉算法每次只能计算一个起点到所有其他顶点的最短路径

  • 若要计算所有顶点对之间的最短路径,需要对每个顶点都运行一次迪杰斯特拉算法,时间复杂度太大

先说几个Floyd算法的要点:Floyd 算法对边的权值正负没有要求,都可以处理。 Floyd算法核心思想是动态规划。

(1)最优子结构性质

(2)动规五部曲

之前在讲解动态规划的时候,给出过动规五部曲:

  • 确定dp数组(dp table)以及下标的含义

这里我们用 grid数组来存图,那就把dp数组命名为 grid。

grid[i][j][k] = m表示 节点i 到 节点j 以[1...k] 集合中的一个节点为中间节点的最短距离为m

why???

        节点i 到 节点j 的最短路径中 一定是经过很多节点,那么这个集合用[1...k] 来表示。

        你可以反过来想,节点i 到 节点j 中间一定经过很多节点,那么你能用什么方式来表述中间这么多节点呢?

        所以 这里的k不能单独指某个节点,k 一定要表示一个集合,即[1...k] ,表示节点1 到 节点k 一共k个节点的集合。

  • 确定递推公式

在上面的分析中我们已经初步感受到了递推的关系。

我们分两种情况:

     1. 节点i 到 节点j 的最短路径经过节点k  grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]

      节点i 到 节点k 的最短距离 不经过节点k,中间节点集合为[1...k-1],所以 表示为grid[i][k][k - 1],同理节点k 到 节点j 的最短距离 也是不经过节点k,表示为 grid[k][j][k - 1]

     2.节点i 到 节点j 的最短路径不经过节点k:grid[i][j][k] = grid[i][j][k - 1]

   


     因为我们是求最短路,对于这两种情况自然是取最小值。

       最终: grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])

  • dp数组如何初始化

grid[i][j][k] = m,表示 节点i 到 节点j 以[1...k] 集合为中间节点的最短距离为m。刚开始初始化k =0;本题求的是最小值,所以输入数据没有涉及到的节点的情况都应该初始为一个最大数。

vector<vector<vector<int>>> grid(n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1, 10005)));  // C++定义了一个三位数组,10005是因为边的最大距离是10^4

for(int i = 0; i < m; i++){
    cin >> p1 >> p2 >> val;
    grid[p1][p2][0] = val;
    grid[p2][p1][0] = val; // 注意这里是双向图
} 
  • 确定遍历顺序

从递推公式:grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]) 可以看出,我们需要三个for循环,分别遍历i,j 和k;

而 k 依赖于 k - 1, i 和j 到 并不依赖与 i - 1 或者 j - 1 等等。


那么这三个for的嵌套顺序应该是什么样的呢?

        我们是把 k =0 的 i 和j 对应的数值都初始化了,这样才能去计算 k = 1 的时候 i 和 j 对应的数值。这就好比是一个三维坐标,i 和j 是平层,而k 是 垂直向上 的。遍历的顺序是从底向上 一层一层去遍历。所以遍历k 的for循环一定是在最外面,这样才能一层一层去遍历。如图

至于遍历 i 和 j 的话,for 循环的先后顺序无所谓。

for (int k = 1; k <= n; k++) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            grid[i][j][k] = min(grid[i][j][k-1], grid[i][k][k-1] + grid[k][j][k-1]);
        }
    }
}

  • 举例推导dp数组

三、最终代码

//所有顶点对之间的最短路径---Floyd 算法
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    vector<vector<vector<int>>>  grid(n+1,vector<vector<int>>(n+1,vector<int>(n+1,10005)));
    //存储图
    for(int i=0;i<m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        grid[u][v][0]=w; //双向图
        grid[v][u][0]=w;
    }
    //开始floyd
    for(int k=1;k<=n;k++) //最外层遍历k
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                //动态规划
                grid[i][j][k]=min(grid[i][k][k-1]+grid[k][j][k-1],grid[i][j][k-1]);
            }
        }
    }
    //输出结果
    int q;
    cin>>q;
    while(q--)
    {
        int sta,end;
        cin>>sta>>end;
        if(grid[sta][end][n]==10005) cout<<"-1"<<endl;
        else cout<<grid[sta][end][n]<<endl;

    }
    return 0;
}

滚动数组优化

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;

    vector<vector<int>> grid(n + 1, vector<int>(n + 1, 10005));  // 因为边的最大距离是10^4

    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
        grid[p2][p1] = val; // 注意这里是双向图

    }
    // 开始 floyd
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]);
            }
        }
    }
    // 输出结果
    int z, start, end;
    cin >> z;
    while (z--) {
        cin >> start >> end;
        if (grid[start][end] == 10005) cout << -1 << endl;
        else cout << grid[start][end] << endl;
    }
}

相关文章:

  • 在 Elasticsearch 中扩展后期交互模型 - 第 2 部分 - 8.18
  • WRF/Chem 模式技术解读:为大气污染治理提供有力支撑
  • 【设计模式】常用的设计模式详解
  • C#Dictionary值拷贝还是引用
  • 项目篇:模拟实现高并发内存池(2)
  • MFC中CString类型是如何怎么转std::string的
  • 基于大模型的下颌前突畸形预测及治疗方案研究报告
  • Trick:vs编译的release中提示debug库找不到方案
  • 报错 - redis - Unit redis.service could not be found.
  • go安装lazydocker
  • MyBatis-Plus:告别手写 SQL 的高效之道
  • 软考-软件设计师-计算机网络
  • Kafka消息自定义序列化
  • Android <queries>声明的作用和配置方法
  • 【yolo】YOLO训练参数输入之模型输入尺寸
  • wordpress表单插件CF7调用方式
  • 启动方法jupyter(Anaconda)
  • 【设计模式】装饰模式
  • Apache Tomcat CVE-2025-24813 安全漏洞
  • AI视频是否会影响原创价值
  • 赵作海因病离世,妻子李素兰希望过平静生活
  • 前4个月我国货物贸易进出口同比增长2.4%,增速较一季度加快1.1个百分点
  • 身临其境感受伟人思想力量,“马克思书房”在上海社科馆揭幕
  • 妻子藏匿一岁幼儿一年多不让丈夫见,法院发出人格权侵害禁令
  • 商务部再回应中美经贸高层会谈:美方要拿出诚意、拿出行动
  • 进化版大巴黎通杀英超,那个男人后悔了吗