多源最短路径是求图中任意两点间的最短路,采用动态规划算法,也称为Floyd算法。将顶点编号为0,1,2...n-1首先定义dis[i][j][k]为顶点 i 到 j 的最短路径,且这条路径只经过最大编号不超过k的顶点。于是我们最终要求的是dis[i][j][n-1].状态转移方程如下:
dis[i][j][k]=min{dis[i][j][k-1],dis[i][k][k-1]+dis[k][j][k-1]};
状态转移方程的解释:在计算dis[i][j][k]的时候,我们考虑 i 到 j 是否要经过顶点 k ,若不经过顶点 k ,那么结果就是 i 到 j 的最大路径经过的最大顶点不超过 k-1,也就是dis[i][j][k-1].倘若 i 到 j 经过顶点 k,那么i到j可以分为两部分之和,即 i 到k 和 k 到 j.这时候子问题最优解是 dis[i][k][k-1]和dis[k][j][k-1],这种情况下得到的原问题最优解是dis[i][k][k-1]+dis[k][j][k-1].所以综合起来: dis[i][j][k]=min{dis[i][j][k-1],dis[i][k][k-1]+dis[k][j][k-1]};
在实现的时候没有必要使用三维数组。可以采用覆盖的方法:从k=0 to n-1 来计算,状态方程改变如下:
dis[i][j]=min{dis[i][j],dis[i][k]+dis[k][j]};
1 #include<iostream> 2 using namespace std; 3 #define MAX_NUMBER INT_MAX/2 4 #define MAX_SIZE 100 5 struct Graph { 6 int V, E; 7 int R[MAX_SIZE][MAX_SIZE]; 8 }; 9 int path[MAX_SIZE][MAX_SIZE]; 10 int dis[MAX_SIZE][MAX_SIZE]; 11 void Floyd(Graph G); 12 void PrintPath(int i, int j); 13 int main() { 14 Graph G; 15 int i, j, w,k; 16 cin >> G.V >> G.E; 17 for (i = 0; i < G.V; i++) 18 for (j = 0; j < G.V; j++) { 19 G.R[i][j] = (i == j ? 0 : MAX_NUMBER); 20 path[i][j]=i; //假设i到j有直接路径 21 } 22 //--------------------------------初始化 23 for (k = 0; k < G.E; k++) { 24 cin >> i >> j >> w; 25 G.R[i][j] = G.R[j][i] = w; 26 } 27 Floyd(G); 28 for (i = 0; i < G.V; i++) { 29 for (j = 0; j < G.V; j++) 30 printf("%3d", dis[i][j]); 31 cout << endl; 32 } 33 printf(" "); 34 for (i = 0; i < G.V; i++) { 35 for (j = 0; j < G.V; j++) 36 printf("%3d", path[i][j]); 37 cout << endl; 38 } 39 PrintPath(0,9); 40 return 0; 41 } 42 void Floyd(Graph G) { 43 int i, j, k; 44 for (i = 0; i < G.V; i++) 45 for (j = 0; j < G.V; j++) 46 dis[i][j] = G.R[i][j]; //初始化 47 for (k = 0; k < G.V; k++) 48 for (i = 0; i < G.V; i++) 49 for (j = 0; j < G.V;j++) 50 if (dis[i][j]>dis[i][k] + dis[k][j]) { //更新 51 path[i][j] = k; 52 dis[i][j] = dis[i][k] + dis[k][j]; 53 } 54 } 55 void PrintPath(int i, int j) { 56 if (i==j) { 57 printf("%3d", i); 58 return; 59 } 60 PrintPath(i,path[i][j]); 61 printf("%3d", j); 62 }