题目给出一个带权有向图,要求出这个图的每两个点之间的最短路径的距离。(v个顶点,e条边)
要求在这个图中不能有负环(否则所有两点间的距离都可以通过这个环来无限减少,计算没有意义)
原始的思想是可以用dijkstra算法一次次去算每两个点之间的距离,但是这样很慢,而且不能直接检测负环。
引入floyd算法,用动态规划的思想来做这道题。
现在我们用A[k][i][j]来表示,经过第1-k个点,从点i到达点j的最短路径;(比如A[3][7][8]表示的是点7要到点8,只能经过点(1,2,3)时的最短路径,比如A[0][1][2]就表示点1和点2不经过其他点,直接相连的距离,即点1和2的直接相连路径的权值)
可以容易想到一点,A[k-1][i][j]和A[k][i][j]的区别在于第k个点能不能用,所以得到了一个递归式:
$$ A[k][i][j]=A[k-1][i][j] $$ 或者 $$ A[k][i][j]=A[k-1][i][k]+A[k-1][k][j] $$ 这两个式子中的较小值就是i到j的最短距离;
其中:
第一个式子 $$ A[k][i][j]=A[k-1][i][j] $$ 的意思是,如果第k个点的使用,不能减少i到j点的距离,那么就不用。(用k点的目的是,希望通过k点做中转,来减少距离)
第二个式子 $$ A[k][i][j]=A[k-1][i][k]+A[k-1][k][j] $$ 表示的意思就是从i点先到k点,再从k点到j点所需要的最短路径的和,这就表示了从i到j点经过k点的最短路径,如果经过k点比不经过k点所需的路径最短,就更新这一条作为最短路径。
对于这个三维数组,其实k这个维度是可以覆盖更新的,因为
$$A[k][i][k]=A[k-1][i][k]+A[k-1][k][k]=A[k-1][i][k]+0=A[k-1][i][k];$$
$$A[k][k][j]=A[k-1][k][j]+A[k-1][k][k]=A[k-1][k][j]+0=A[k-1][k][j];$$
代入到上面的递归式中则可以得到
$$min(A[k-1][i][j],A[k-1][i][k]+A[k-1][k][j])$$
我们可以这么理解,对于A[i][j]这个二维数组,存储的是题给图的邻接矩阵,两点连通的时候存储的值为权值,两点不可达的时候存储的值为infinity。然后我们对这个二维数组进行k次更新(k=v),每一次更新的意义是:在第k次更新时,A[i][j]的意义为A[k][i][j],即在只经过(1,2,3...,k)这些点时,i点要到j点所可以走的最短路径的距离。在还没有更新的时候,即两点之间不经过其他任何中转点,直接从i点到j点,所以没有更新这个矩阵的时候,矩阵内存储的就是邻接矩阵。在第v次更新完成后,矩阵内存储的就是点i到点j所可能走的最短路径的距离。
所以我们可以想到,在第k次和第k-1次更新时,这个矩阵中每一对点的变化:对于i到j的最短距离,现在可以用的点除了1~k-1,还有了点k,所以我们去尝试点i先到k,再从点k到j这两个最短路径路径的和,如果经由点k可以获得一个更短的路径,那么我就把它更新为最短路径。
所以A在更新k值的时候可以用前一个值覆盖掉,直接把这个值存在A数组中。这也是为何在前面的递归式中,我们去关心k和k-1之间的关系。
ac代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int maxx = 110;
const LL inf = ((1LL) << 32);
LL A[maxx][maxx];
int v,e;
void floyd(){
for(int k=0;k<v;k++){
for(int i=0;i<v;i++){
if(A[i][k]==inf) continue;
for(int j=0;j<v;j++){
if(A[k][j]==inf) continue;
A[i][j]=min(A[i][j],A[i][k]+A[k][j]);
}
}
}
return ;
}
int main (){
int x,y,z;
cin>>v>>e;
for(int i=0;i<maxx;i++){
for(int j=0;j<maxx;j++)
A[i][j]= i==j?0:inf ;
}
while(e--){
cin>>x>>y>>z;
A[x][y]=z;
}
floyd();
bool negative = false;
for (int i = 0; i < v; i++)
if (A[i][i] < 0)
negative = true;
if (negative)
printf("NEGATIVE CYCLE\n");
else {
for (int i = 0; i < v; i++) {
for (int j = 0; j < v; j++) {
if (A[i][j] == inf)
printf("INF");
else
printf("%lld", A[i][j]);
printf("%c", j == v - 1 ? '\n' : ' ');
}
}
}
return 0;
}
错点:
1.数据的infinity要注意,不用ll的话也可以用INT_MAX(最大值应该是2*10^7*100)