一、什么是最短路
顾名思义,就是指从结点 s s s到结点 t t t的最短路径,通常记为 δ ( s , t ) \delta(s, t) δ(s,t)
二、解决最短路的方法
如下图表格:
单源最短路径(起点固定) | 全源最短路径(起点随意) | |
---|---|---|
无负权边 | Dijkstra算法( O ( V log 2 E ) O( V\log_2E) O(Vlog2E)) | Floyd算法( O ( V 3 ) O(V^3) O(V3))/Johnson算法 |
有负权边 | SPFA算法( O ( V E ) ) O(VE)) O(VE)) | 同上 |
由于Johnson算法太过复杂,我们最后讲解。
三、Floyd算法详解:
Floyd算法是一种暴力算法,算法时间复杂度 O ( ∣ V ∣ 3 ) O(|V|^3) O(∣V∣3),下面是过程详解:
Floyd算法运用了动态规划思想,求两点间的距离,可以分两种情况,一种是经过某个点
k
k
k的路径,另一种是不经过点
k
k
k的路径。
动态规划的过程可描述为:
1.令
k
=
1
k = 1
k=1,求出所有节点间的最短路径。
2.令
k
=
2
k = 2
k=2,求出所有节点中的路径,与步骤一中结果所比较,进行更新。
⋯
\cdots
⋯
注意,Floyd算法最好使用邻接矩阵,除非两点间有重边或其余问题,适得其反不能使用邻接矩阵。
Floyd代码:
// 此处省略邻接矩阵储存图的步骤。
void floyd() {
for (int i = 1; i <= n; i++)
for (int s = 1; s <= n; s++)
for (int t = 1; t <= n; t++)
if (graph[i][j] > graph[i][k] + graph[k][j])
graph[i][j] = graph[i][k] + graph[k][j];
}
学到这里,我们来挑战一下一道例题:
洛谷—采购特价商品
由于 n n n的范围较小,所以我们可以采用Floyd算法,这道题的坑点就在于他没直接给出两点间距离,所以我们介绍一个函数:
double hypot(double x, double y);
这个函数是计算 x 2 + y 2 \sqrt{x^2 + y^2} x2+y2的,我们只需传参是传入连点坐标差,即可得出两点间距离。此函数存储在cmath库中,我们需要先导入头文件。所以标准代码如下:
#include <iostream>
#include <cmath>
using namespace std;
struct point {
int x, y;
};
int main() {
int n;
cin >> n;
point ps[n + 1];
double g[n + 1][n + 1]; // 此处卡常,数组尽量开小一点。
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = 100000;
for (int i = 1; i <= n; i++) cin >> ps[i].x >> ps[i].y;
int m;
cin >> m;
while (m--) {
int u, v;
cin >> u >> v;
g[u][v] = g[v][u] = hypot(ps[u].x - ps[v].x, ps[u].y - ps[v].y); // 无向图存两条边
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
g[j][k] = min(g[j][k], g[j][i] + g[i][k]); // 简写代码
int s, t;
cin >> s >> t;
printf("%.2lf", g[s][t]);
}