C++图论基础多源最短路-Floyd 算法流食般投喂


多源最短路:即图中每对顶点间的最短路径。
📌这里多源最短路算法我们只介绍 Floyd(弗洛伊德) 算法。
📌它适用于任何图,不管是有向无向,边权正负,但是最短路必须存在(也就是不存在负环),但其实 Floyd 算法是可以判断负环的。
📌Floyd 算法本质是动态规划,用来求任意两个结点之间的最短路,也称插点法。通过不断在两点之间加入新的点当作桥梁,来更新最短路;也就是在起点、终点之间,不断建桥梁,看看在总路线中走不走这个新建桥梁会不会缩短驾驶路程。
💡这里给出动态规划的六板斧(加上空间优化):
1、状态表示:f[k][i][j] 表示:仅仅经过 [1, k] 这些点建起的桥梁,结点 i 走到结点 j 的最短路径的长度。
2、状态转移方程:
📌第一种情况:不选新来的点(不走刚刚建起的桥梁,从起点 i 走到 终点 j ,只走编号为 1 ~ k - 1 的桥梁):f[k][i][j] = f[k - 1][i][j];
📌第二种情况:选新来的点(走刚刚新建起的桥梁,从起点 i 走到终点 j ,以编号为 k 的桥梁为中转,划分成 i 到 k 、k 到 j 两段,这两段行程可走的桥梁范围都是:1 ~ k - 1):f[k][i][j] = f[k - 1][i][k] + f[k - 1][k][j];
📌最终是求两种情况的 min 。
3、空间优化:
dp 表 f 数组本身是三维的,让桥梁 k 那一维作 z 轴,我们可以发现,k = 0 的那一层是初始化的时候,k = n 的那一层是最终结果的时候,当前层的结果是根据上一层的结果递推的,所以可以空间优化、删掉第一维,而且,根据 k 值的不同,每一层更新出来的结果根据状态表示含义都有着自己的意义,比如:k = 4 的时候,f[4][i][j] 表示的是:从起点 i 到 终点 j 只经过 编号为1 ~ 4的桥梁 的最短路径长度。
4、初始化:
和邻接矩阵的初始化几乎一摸一样。
📌f[i][i] = 0;自己到自己的最短路径是 0.
📌f[i][j] 为初始状态下 i 到 j 的距离,如果 i、j 之间没有边,距离则是无穷。
5、填表顺序
一定先枚举 k ,再枚举 i 和 j ,因为当前第 k 层的更新,靠的都是 k - 1 层的状态来的。
6、最终结果:
dp 表里面存着。
OJ题来源:洛谷
OJ题名:【模板】Floyd
OJ题归属:图论基础【多源最短路】
解题算法:Floyd 算法。
#include<iostream> #include<cstring> using namespace std; const int N = 110; int n, m; int f[N][N]; int main() { cin >> n >> m; // 初始化 memset(f, 0x3f, sizeof f); for (int i = 1; i <= n; i++) f[i][i] = 0; for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; // 重边 f[u][v] = f[v][u] = min(f[u][v], w); } // Floyd for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { f[i][j] = min(f[i][j], f[i][k] + f[k][j]); } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cout << f[i][j] << " "; } cout << endl; } return 0; }
更多推荐


所有评论(0)