当前位置: 首页 > 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^4for(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=1; i<=n; i++) grid[i][i][0] = 0; //自己到自己是0//存储图for(int i=0;i<m;i++){int u,v,w;cin>>u>>v>>w;if(w<grid[u][v][0]) //处理重边(有时候题目会说没有重边)grid[u][v][0]=w; //双向图grid[v][u][0]=w;}//开始floydfor(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^4for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;grid[p1][p2] = val;grid[p2][p1] = val; // 注意这里是双向图}// 开始 floydfor (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;}
}


http://www.mrgr.cn/news/95788.html

相关文章:

  • 利用dify打造命令行助手
  • Spring Boot整合Activiti工作流详解
  • 【Redis实战专题】「技术提升系列」​RedisJSON核心机制与实战应用解析(入门基础篇)
  • 调语音类大模型必备-音频录制小妙招-自制工具-借助浏览器录一段单声道16000采样率wav格式音频
  • 华为OD机试 - 核酸最快检测效率 - 动态规划、背包问题(Java 2024 E卷 200分)
  • 【学习记录】大模型微调之使用 LLaMA-Factory 微调 Qwen系列大模型,可以用自己的数据训练
  • How to share files with Linux mint 22 via samba in Windows
  • Sql Server 索引性能优化 分析以及分表
  • _DISPATCHER_HEADER结构中的WaitListHead和_KWAIT_BLOCK的关系
  • Linux的SPI子系统的原理和结构详解【SPI控制器(spi_master)、SPI总线(device-driver-match匹配机制)、SPI设备、SPI万能驱动`spidev.c`】
  • Unity 实现一个简易可拓展性的对话系统
  • 深度解读DeepSeek:开源周(Open Source Week)技术解读
  • 从零开始的LeetCode刷题日记:128. 最长连续序列
  • Spring Boot 整合 Nacos 注册中心终极指南
  • CentOS 7 更换 yum 源(阿里云)+ 扩展 epel 源
  • Jackson实现JSON数据的合并
  • vivo 湖仓架构的性能提升之旅
  • AI本地部署之dify
  • Redis 服务搭建
  • DeepSeek面试——模型架构和主要创新点