文章目录
一、floyd 算法
1. floyd 算法简介
2. floyd 算法核心思想
3. floyd 算法实现步骤
4. floyd 算法具体实现详见例题 floyd 求最短路。
二、floyd 算法与其他算法的总结复习(重点)
1. Dijkstra 算法-朴素 O(n*n)
2. Dijkstra 算法-堆优化 O(mlogm)
3. Bellman-ford 算法 O(nm)
4. spfa 算法 O(n)~O(nm)
5. floyd 算法 O(n*n*n)
三、floyd 算法例题—— floyd 求最短路
具体实现
1. 样例演示
2. 实现思路
3. 代码注解
4. 实现代码
一、floyd 算法
1. floyd 算法简介
floyd 算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与 Dijkstra 算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
floyd 算法是一种在具有正或负边缘权重(但没有负周期)的加权图中找到最短路径的算法。
floyd 算法的单个执行将找到所有顶点对之间的最短路径的长度(加权)。 虽然它不返回路径本身的细节,但是可以通过对算法的简单修改来重建路径。
优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。
缺点:时间复杂度比较高,不适合计算大量数据。
2. floyd 算法核心思想
(1) 邻接矩阵(二维数组)dist 储存路径,数组中的值开始表示节点和节点之间初始直接路径,最终是节点和节点之间的最小路径,有两点需要注意的,第一是如果没有直接相连的两点那么默认为一个很大的值(不要因为计算溢出成负数),第二是自己和自己的距离要为 0。
(2) 从第 1 个到第 n 个点依次加入松弛计算,每个点加入进行试探枚举是否有路径长度被更改(自己能否更新路径)。顺序加入( k 枚举)松弛的点时候,需要遍历图中每一个点对( i , j )双重循环),判断每一个点对距离是否因为加入的点而发生最小距离变化,如果发生改变(变小),那么两点( i , j )距离就更改。
(3) 重复上述直到最后插点试探完成。
其中第 2 步的状态转移方程为:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
其中 dp[a][b]
的意思可以理解为节点 a 到节点 b 的最短路径,所以 dp[i][k]
的意思可以理解为节点 i 到节点 k 的最短路径, dp[k][j]
的意思为节点 k 到节点 j 的最短路径。
3. floyd 算法实现步骤
- (1) 在主函数中创建一个矩阵,存储输入的两点间的距离。
- (2) 在 floyd 函数中,初始化记录最短距离的矩阵和记录中介点的矩阵。初始化之后将主函数的矩阵复制给记录最短距离的矩阵。
- (3) 用三层循环不断更新最短距离。
4. floyd 算法具体实现详见例题 floyd 求最短路。
二、floyd 算法与其他算法的总结复习(重点)
1. Dijkstra 算法-朴素 O(n*n)
(1) 初始化距离数组, dist[1] = 0, dist[i] = 0x3f3f3f3f。
(2) for n 次循环 每次循环确定一个 min 加入 S 集合中,n 次之后就得出所有的最短距离。
(3) 将不在 S 中 dist_min 的点 ->t。
(4) t->S 加入最短路集合。
(5) 用 t 更新到其他点的距离
2. Dijkstra 算法-堆优化 O(mlogm)
- (1) 利用邻接表,优先队列。
- (2) 在 priority_queue<PII,vector,greater> heap; 中将返回堆顶。
- (3) 利用堆顶来更新其他点,并加入堆中类似广度优先搜索。
3. Bellman-ford 算法 O(nm)
- (1) 注意连锁想象需要备份,struct Edge{inta,b,c} Edge[M]。
- (2) 初始化 dist, 松弛 dist[x.b] = min(dist[x.b], backup[x.a]+x.w)。
- (3) 松弛 k 次,每次访问 m 条边。
4. spfa 算法 O(n)~O(nm)
- (1) 利用队列优化仅加入修改过的地方。
- (2) for k 次。
- (3) for 所有边利用宽搜模型去优化 bellman-ford 算法。
- (4) 更新队列中当前点的所有出边。
5. floyd 算法 O(nnn)
- (1) 初始化 d 。
- (2) k,i,j 去更新 d 。
三、floyd 算法例题—— floyd 求最短路
题目描述
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 impossible
。
数据保证图中不存在负权回路。
输入格式
第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
接下来 k 行,每行包含两个整数 x,y,表示询问点 x 到点 y 的最短距离。
输出格式
共 k 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible
。
数据范围
1 ≤ n ≤ 200
1 ≤ k ≤ n2
1 ≤ m ≤ 20000
图中涉及边长绝对值均不超过 10000。
输入样例
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例
impossible
1
具体实现
1. 样例演示
首先,输入 n=3,m=3,k=2;表示图有 3 个点,3 条边,询问 2 次。
第一条边是从节点 1 到节点 2 ,边长为 1。
第二条边是从节点 2 到节点 3 ,边长为 2。
第三条边是从节点 1 到节点 3 ,边长为 1。
第一次询问,节点 2 到节点 1 是否存在路径?
第二次询问,节点 1 到节点 3 是否存在路径?
显然,第一次询问结果 impossible。
第二次询问结果 1。
2. 实现思路
- 详见上文核心思想和实现步骤
3. 代码注解
- floyd 本身是floyd 本身是个动态规划算法,在代码实现的时候省去了一维状态。
原状态是:f[i, j, k]表示从 i 走到 j 的路径上除了 i , j 以外不包含点 k 的所有路径的最短距离。那么f[i, j, k] = min(f[i, j, k - 1), f[i, k, k - 1] + f[k, j, k - 1]。
因此在计算第 k 层的 f[i, j] 的时候必须先将第 k - 1 层的所有状态计算出来,所以需要把 k 放在最外层。
4. 实现代码
#include <bits/stdc++.h> using namespace std; const int N = 210, INF = 1e9; int n, m, Q; int d[N][N]; void floyd() { for (int k = 1; k <= n; k ++ ) { for (int i = 1; i <= n; i ++ ) { for (int j = 1; j <= n; j ++ ) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } } } int main() { cin >> n >> m >> Q; for (int i = 1; i <= n; i ++ ) { for (int j = 1; j <= n; j ++ ) { if (i == j) { d[i][j] = 0; } else { d[i][j] = INF; } } } while (m -- ) { int a, b, c; cin >> a >> b >> c; d[a][b] = min(d[a][b], c); } floyd(); while (Q -- ) { int a, b; cin >> a >> b; int t = d[a][b]; if (t > INF / 2) { puts("impossible"); } else { cout << t << endl; } } system("pause"); return 0; }