GRL_1_C:All Pairs Shortest Path

题目链接

题目给出一个带权有向图,要求出这个图的每两个点之间的最短路径的距离。(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)


猜你喜欢

转载自blog.csdn.net/qq_33982232/article/details/80948114