问题
求解任意两点间最短路问题也叫多源最短路径问题。
可解决途径
一种方法是把图中每个点当做源点重复算n次Dijkstra 算法(Dijkstra是求单源最短路径的算法),时间复杂度O(n^3),据说可以优化成O(n^2logn)。
另一种方法时最经典的算法Floyd算法,时间复杂度也是O(n^3),但是关键代码只有5行,适合时间要求不苛刻的时候编写。
Floyd算法基本思想
Floyd算法本质上是一个动态规划算法。对于每个顶点k,枚举其他两个顶点i和j,若i和j之间的距离大于i到k加上k到j的距离,那么更新i到j之间的距离。
【注】Floyd算法允许图中有带负权值的边,但不允许有包含带负权值的边组成的回路。(这句话摘自书上,本人并未深刻理解,若有大佬理解,望指教一二)
基本操作
d[k][i][j] 表示 顶点i 经过顶点k 到顶点j 的最短路径长度。
分两种情况讨论:
-
经过顶点k, d[k][i][j] = d[k-1][i][j]。 即等于只用前k-1个顶点时的最短路径
-
不经过顶点k, d[k][i][j] = d[k-1][i][k] + d[k-1][k][j]。 即等于i离k的最路路径+k离j的最短路径。
可以得到递推式 d[k][i][j] = min( d[k-1][i][j] , d[k-1][i][k] + d[k-1][s][k] );
还可优化
由于第k层只与k-1层相关,也可以用二维表示:
(本图来自知乎,放在此处便于大家理解,侵删)
d[i][j] = min( d[i][j] , d[i][k] + d[k][j] );
精简版算法模板:
#include <bitsstdc++.h>
using namespace std;
#define INF 2147483647
#define MAX_V 1000
#define MAX_E 2000
int d[MAX_V][MAX_V]; // d[u][v]表示边u->v的距离(不存在时设为INF,d[i][i] = 0)
int V; // 顶点数
void warshall_floyd(){
for(int k = 0;k < V; k++){
for(int i = 0;i < V; i++){
for(int j = 0;j < V; j++){
if(i != j && i != k && j != k)
if(d[i][k] != INF && d[k][j] != INF) //INF与正数相加会溢出
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
int main(){
}