Floyd算法分析:
为了求出图中任意两点间的最短路径,当然可以把每个点作为起点,求解N
次单源最短路径问题。
不过,在任意两点间最短路问题中,图一般比较稠密。使用 Floyd算法 可以在 O(N^3)
的时间内完成求解,并且程序实现非常简单。
设d[k,i,j]
表示“经过若干个编号不超过k
的节点”从i
到 j
的最短路长度。
该问题可划分为两个子问题:经过编号不超过k-1
的节点从i
到 j
,或者从i
先到k
再到j
。
于是:
d[k,i,j]= min(d[k-1,i,j],d[k-1,i,k]+d[k-1,k,j])
初值为 d[0,i,j] = g[i,j]
,其中 g
为定义的邻接矩阵。
可以看到, Floyd算法的本质是动态规划。
k
是阶段,所以必须置于最外层循环中。i
和 j
是附加状态,所以应该置于内层循环。
以上也解释了为何很多初学者按照 i
, j
,k
的顺序执行循环,会得到错误的结果。
与背包问题的状态转移方程类似,k
这一维可被省略。最初,我们可以直接用d
保存邻接矩阵,然后执行动态规划的过程。
当最外层循环到 k
时,内层有状态转移:d[i,j]= min(d[i,j], d[i,k]+d[k,j])
最终 d[i,j]
就保存了 i
到 j
的最短路长度。
代码片段:
//初始化:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=0;
else d[i][j]=inf;
// floyd算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
例题:AcWing 854. Floyd求最短路
时间复杂度:
O(n^3) , n
表示点数
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
#define inf 0x3f3f3f3f
int d[N][N];
int n, m, k;
void floyd()
{
for(int k=1; k<=n; ++k)
{
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
}
}
}
}
int main()
{
cin>>n>>m>>k;
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
if(i==j) d[i][j] = 0;
else d[i][j] = inf;
while(m--)
{
int x, y, z;
cin>>x>>y>>z;
d[x][y] = min(d[x][y], z);
}
floyd();
while(k--)
{
int a, b;
cin>>a>>b;
cout<<(d[a][b]>inf/2 ? "impossible" : to_string(d[a][b]))<<endl;
}
return 0;
}