问题描述
设
G
=
(
V
,
E
)
G=(V,E)
G=(V,E)是一个有
n
n
n个结点的带权有向图,
w
(
i
,
j
)
w(i,j)
w(i,j)是权函数
w
(
i
,
j
)
=
{
边
<
i
,
j
>
上
的
权
值
if
<
i
,
j
>
∈
E
0
if
i
=
j
∞
if
<
i
,
j
>
∉
E
w(i,j)=\begin{cases} 边<i,j>上的权值 &\text{if } <i,j>\in E \\ 0 &\text{if } i=j\\ \infty &\text{if } <i,j>\notin E \end{cases}
w(i,j)=⎩⎪⎨⎪⎧边<i,j>上的权值0∞if <i,j>∈Eif i=jif <i,j>∈/E
每对结点间的最短路径问题是指图中任意一对结点
i
i
i和
j
j
j之间的最短路径。
分析
Dijkstra算法要求图中的边的权为非负值,而本问题中允许边的权为负值,但不允许路径长度为负值的回路,因为若结点 i i i到结点 j j j的路径上存在负值回路,则意味着结点 i i i到结点 j j j没有最短路径。
最优子结构特性
设 G = ( V , E ) G=(V,E) G=(V,E)是带权有向图,L(i,j)是从结点 i i i到结点 j j j的最短路径长度, k k k是这条路径上的一个结点, L ( i , k ) L(i,k) L(i,k)和 L ( k , j ) L(k,j) L(k,j)分别是从 i i i到 k k k和从 k k k到 j j j的最短路径长度,则必有 L ( i , j ) = L ( i , k ) + L ( k , j ) L(i,j)=L(i,k)+L(k,j) L(i,j)=L(i,k)+L(k,j),若不然,则 L ( i , j ) L(i,j) L(i,j)代表的路径就不是最短路径。
最优解值的递推关系
d
−
1
[
i
]
[
j
]
=
{
w
(
i
,
j
)
if
<
i
,
j
>
∈
E
∞
if
<
i
,
j
>
∉
E
d_{-1}[i][j]=\begin{cases} w(i,j) &\text{if } <i,j>\in E \\ \infty &\text{if } <i,j>\notin E \end{cases}
d−1[i][j]={w(i,j)∞if <i,j>∈Eif <i,j>∈/E
d
k
[
i
]
[
j
]
=
min
{
d
k
−
1
[
i
]
[
j
]
,
d
k
−
1
[
i
]
[
k
]
+
d
k
−
1
[
k
]
[
j
]
}
,
1
≤
k
≤
n
−
1
d_k[i][j]=\min\{d_{k-1}[i][j],d_{k-1}[i][k]+d_{k-1}[k][j]\},1\leq k \leq n-1
dk[i][j]=min{dk−1[i][j],dk−1[i][k]+dk−1[k][j]},1≤k≤n−1
其中
d
k
[
i
]
[
j
]
d_k[i][j]
dk[i][j]表示从结点
i
i
i到结点
j
j
j的路径上,只允许包含编号不大于
k
k
k的结点时,所以可能的路径中的最短路径的长度,
d
−
1
[
i
]
[
j
]
d_{-1}[i][j]
d−1[i][j]表示从
i
i
i到
j
j
j不包含结点(直达)的长度,
L
(
i
,
j
)
=
d
n
−
1
[
i
]
[
j
]
L(i,j)=d_{n-1}[i][j]
L(i,j)=dn−1[i][j]
重叠子问题
为了计算 d k [ i ] [ j ] d_k[i][j] dk[i][j]时,必须计算 d k − 1 [ i ] [ j ] , d k − 1 [ i ] [ k ] , d k − 1 [ k ] [ j ] d_{k-1}[i][j],d_{k-1}[i][k],d_{k-1}[k][j] dk−1[i][j],dk−1[i][k],dk−1[k][j]
- 邻接矩阵 a a a存储有向图
- 二维数组 d d d用于保存各条最短路径的长度,其中 d [ i ] [ j ] d[i][j] d[i][j]存放从结点 i i i到结点 j j j的最短路径的长度
- 二维数组 p a t h path path记录相应的最短路径, p a t h [ i ] [ j ] path[i][j] path[i][j]给出从结点 i i i到结点 j j j的最短路径中的前一个结点,可以反向追溯最短路径
- 初始时 d [ i ] [ j ] = a [ i ] [ j ] d[i][j]=a[i][j] d[i][j]=a[i][j]
- 令 k = 0 , 1 , . . . , n − 1 k=0,1,...,n-1 k=0,1,...,n−1,每次考察一个结点 k k k
- 在算法的第 k k k步上应作出决策:从 i i i到 j j j的最短路径上是否包含结点 k k k
程序
#include<iostream>
using namespace std;
vector<vector<int> > a;//邻接矩阵
vector<vector<int> > d;//保存每对结点之间最短路径
vector<vector<int> > path;//标记函数
int n;//结点数
//创建邻接矩阵
void CreateA(){
cin >> n;
for(int i = 0;i < n;i ++){
vector<int> v;
int v1;
for(int j = 0;j < n;j ++){
cin >> v1;
v.push_back(v1);
}
a.push_back(v);
}
}
void allPath(){
for(int i = 0;i < n;i ++){//初始化d
for(int j = 0;j < n;j ++){
d[i][j] = a[i][j];
}
}
//迭代:对于点k,若i直接到j的距离大于1->k->j的距离和时,改写d[i][j]
for(int k = 0;k < n;k ++){
for(int i = 0;i < n;i ++){
for(int j = 0;j < n;j ++){
if(d[i][k]+d[k][j]<d[i][j])
d[i][j] = d[i][k] + d[k][j];
}
}
}
}
int main(){
CreateA();
allPath();
return 0;
}
结束语
其实你并没有什么忘不掉的人,只是始终对自己那场没有结果的付出和被浪费的爱耿耿于怀。
作者:花城