3.4. 带权有向图中任意两点间的最短路径
1.问题的理解与描述
我们的问题是要找出图G中的每一个顶点到其他所有顶点的距离。此处,顶点i、j间的距离定义为从i出发到j的最短路径长度。这是一个组合优化问题,从i出发到j可能有若干条路径,每条路径都有其长度,目标是找到i到j的最短路径长度。问题形式化为:
输入:表示带权有向图 G=<V,E> 的n*n矩阵W。
输出:对任意的i,j∈V
,i到j间的距离及最短路径。
2. 定理
设G=<V, E>
的两个不同的顶点i,j∈V={1, 2, …, n}
,p是从i到j其间仅经过{1, 2, …, k}的最短路径。
(1)若p不经过顶点k,则p也是从i到达j其间仅经过{1, 2, …, k-1}的最短路径。
(2)若p经过顶点k,即p:i>k>j。我们把前半段i > k记为p1,后半段k>j 记为p2。则p1是从i 到k其间仅经过{1, 2, …, k-1}的最短路径,p2是k到j其间仅经过{1, 2, …, k-1}的最短路径。
定义 dki,j 为i到j且其间仅经过{1, 2, …, k}的最短路径长度。根据上述的任意点对其间最短路径问题的最优子结构特性,有关于di,jk的递归式:
由此递归式的定义可知子问题具有重叠性。
记:
FLOYD-WARSHALL(W)
1 D←W
2 for k←1 to n
3 do for i←1 to n
4 do for j←1 to n
5 if D[i, j]>D[i, k]+ D[k, j]
6 then D[i, j] ←D[i, k]+ D[k, j]
7 return D
显然,算法的时间复杂度为Θ(n3)。
4.构造一个最优解
为了构造最优解—— 任意顶点对(i, j)的最短路径,必须记录路径上的顶点序列。我们用跟踪路径上每一个顶点的前驱来达到这一目的: π(k)ij 定义为从顶点i到j的中间顶点均在集合{1, 2,…, k}中的最短路径上顶点j的前驱。则
那么 π(n)ij 就是顶点对(i,j)的最短路径中j 的前驱顶点
则可修改算法同时得到D,π矩阵
FLOYD-WARSHALL(W)
1 for i←1 to n
2 do for j←1 to n
3 do if i=j or W[i, j]=
4 then Π[i, j] ←NIL
5 else Π[i, j] ←i
6 D←W
7 for k←1 to n
8 do for i←1 to n
9 do for j←1 to n
10 if D[i, j]>D[i, k]+ D[k, j]
11 then D[i, j] ←D[i, k]+ D[k, j]
12 Π[i, j] ←Π[k,j]
13 return D, Π
构造顶点对(i,j) 的最短路径
PRINT-ALL-PAIRS-SHORTEST-PATHS(Π, i, j)
1 if i=j
2 then print i
3 else if Π[i, j]=NIL
4 then print "no path from" i "to" j "exists"
5 else PRINT-ALL-PAIRS-SHORTEST-PATHS(Π,i, Π[i, j])
6 print j
#include "stdafx.h"
#include "FloydWarshall.h"
pair<vector<double>, vector<int>> floydWarshall(const vector<double> &w)
{
int i, j, k, n = sqrt(w.size());
vector<int>pi(w.size(), 0);//最短路径顶点j前驱
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (i == j || w.at(i*n + j) >= DBL_MAX)//i=j or w[i,j]=∞
pi.at(i*n + j) = -1;//pi[i,j]=NIL
else
pi.at(i*n + j) = i; //pi[i,j]=i
vector<double>D(w);//最短路径长度 D=W
for (k = 0; k < n; k++)
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (D.at(i*n + j) > (D.at(i*n + k) + D.at(k*n + j)))//D[i,j]>D[i,k]+D[k,j]
{
D.at(i*n + j) = D.at(i*n + k) + D.at(k*n + j);//D[i,j]=D[i,k]+D[k,j]
pi.at(i*n + j) = pi.at(k*n + j);//pi[i,j]=pi[k,j]
}
return make_pair(D, pi);
}
void printAllPairsShortestPaths(const vector<int>pi, int i, int j)
{
int n = sqrt(pi.size());
if (i == j)
{
cout << i + 1;
return;
}
else if (pi.at(i*n + j) == -1)//pi[i,j]=NIL
cout << "no path from" << i + 1 << "to" << j + 1 << "exists";
else
{
printAllPairsShortestPaths(pi, i, pi.at(i*n + j));
cout << j + 1;
}
}
FloydWarshall.h文件
#ifndef _FLOYDWARSHALL_H
#define _FLOYDWARSHALL_H
#include <cstring>
#include <cfloat>
#include <cstdio>
#include <utility>
#include <vector>
#include <iostream>
using namespace std;
pair<vector<double>, vector<int>> floydWarshall(const vector<double> &w);
void printAllPairsShortestPaths(const vector<int>pi, int i, int j);
#endif
测试main.cpp文件
// FloydWarshall.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "FloydWarshall.h"
//#include <utility>
//using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
double a[] = { 0, 3, 8, DBL_MAX, -4,
DBL_MAX, 0, DBL_MAX, 1, 7,
DBL_MAX, 4, 0, DBL_MAX, DBL_MAX,
2, DBL_MAX, -5, 0, DBL_MAX,
DBL_MAX, DBL_MAX, DBL_MAX, 6, 0 };
vector<double> w(a,a+25);
int n = (int)sqrt(w.size());
pair<vector<double>, vector<int>> r = floydWarshall(w);
//print d
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
cout << r.first.at(i*n + j)<<" ";
cout << endl;
}
//print pi
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
cout << r.second.at(i*n + j) << " ";;
cout << endl;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
printAllPairsShortestPaths(r.second, i, j);
cout <<":"<< r.first.at(i*n + j)<<endl;
}
}
system("pause");
return 0;
}
运行结果:
0 1 -3 2 -4
3 0 -4 1 -1
7 4 0 5 3
2 -1 -5 0 -2
8 5 1 6 0
-1 2 3 4 0
3 -1 3 1 0
3 2 -1 1 0
3 2 3 -1 0
3 2 3 4 -1
1:0
15432:1
1543:-3
154:2
15:-4
241:3
2:0
243:-4
24:1
2415:-1
3241:7
32:4
3:0
324:5
32415:3
41:2
432:-1
43:-5
4:0
415:-2
541:8
5432:5
543:1
54:6
5:0
请按任意键继续…
参考:《算法设计,分析与实现:C、C++和Java》