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

多源最短路:即图中每对顶点间的最短路径。


📌这里多源最短路算法我们只介绍 Floyd(弗洛伊德) 算法。

📌它适用于任何图,不管是有向无向,边权正负,但是最短路必须存在(也就是不存在负环),但其实 Floyd 算法是可以判断负环的。

📌Floyd 算法本质是动态规划,用来求任意两个结点之间的最短路,也称插点法。通过不断在两点之间加入新的点当作桥梁,来更新最短路;也就是在起点、终点之间,不断建桥梁,看看在总路线中走不走这个新建桥梁会不会缩短驾驶路程。


💡这里给出动态规划的六板斧(加上空间优化):

1、状态表示:fkij 表示:仅仅经过 1, k 这些点建起的桥梁,结点 i 走到结点 j 的最短路径的长度。

2、状态转移方程:

📌第一种情况:不选新来的点(不走刚刚建起的桥梁,从起点 i 走到 终点 j ,只走编号为 1 ~ k - 1 的桥梁):fkij = fk - 1ij

📌第二种情况:选新来的点(走刚刚新建起的桥梁,从起点 i 走到终点 j ,以编号为 k 的桥梁为中转,划分成 i 到 k 、k 到 j 两段,这两段行程可走的桥梁范围都是:1 ~ k - 1):fkij = fk - 1ik + fk - 1kj

📌最终是求两种情况的 min 。

3、空间优化:

dp 表 f 数组本身是三维的,让桥梁 k 那一维作 z 轴,我们可以发现,k = 0 的那一层是初始化的时候,k = n 的那一层是最终结果的时候,当前层的结果是根据上一层的结果递推的,所以可以空间优化、删掉第一维,而且,根据 k 值的不同,每一层更新出来的结果根据状态表示含义都有着自己的意义,比如:k = 4 的时候,f4ij 表示的是:从起点 i 到 终点 j 只经过 编号为1 ~ 4的桥梁 的最短路径长度。

4、初始化:

和邻接矩阵的初始化几乎一摸一样。

📌fii = 0;自己到自己的最短路径是 0.

📌fij 为初始状态下 i 到 j 的距离,如果 i、j 之间没有边,距离则是无穷。

5、填表顺序

一定先枚举 k ,再枚举 i 和 j ,因为当前第 k 层的更新,靠的都是 k - 1 层的状态来的。

6、最终结果:

dp 表里面存着。


OJ题来源:洛谷

OJ题名:【模板】Floyd

OJ题归属:图论基础【多源最短路】

解题算法:Floyd 算法。

cpp 复制代码
#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;
}