在概述中,我们谈到最短路径的一个关键性质:最短路径的子路径也是最短路径。那么任意结点之间的最短路问题就可以通过这个性质利用DP来解决。
任意结点对最短路问题
分析:
按照DP的三个步骤:
1.刻画最优解的结构特征:
定义dp[k][i][j]为只使用0~k和i,j的情况下,i到j的最短路长度。当k=0,则只使用i和j,则dp[0][i][j]=cost[i][j]。2.递归地定义最优解:
只使用0~k时,我们分i和j的最短路正好经过顶点k一次和完全不经过顶点k两种情况来讨论。
1.不经过k,则dp[k][i][j] = dp[k-1][i][j]。
2.经过k,则dp[k][i][j] = dp[k-1][i][k] + dp[k-1][k][j]。
那么dp[k][i][j] = min(dp[k-1][i][j] , dp[k-1][i][k] + dp[k-1][k][j])3.计算最优解的值,采用自底向上的递推法。
因为使用递推的方式,我们可以简化成dp[i][j],类似于背包问题。
Floyd-Warshall算法:
void warshall_floyd()
{
for(int k=0; k<V; k++)
{
for(int i=0; i<V; i++)
{
for(int j=0; j<V; j++)
{
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
return;
}
注意初始化情况,这里我们用邻接矩阵来实现图的建立,当结点之间边不存在时设为INF,且dp[i][i]=0,其他dp[i][j] = cost[i][j]。
完整代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10000;
const int INF = 1000001;
int V,E;
int d[maxn][maxn];
int cost[maxn][maxn];
/*建图*/
void build_map()
{
memset(cost, 0, sizeof(cost));
scanf("%d%d",&V,&E);
int s,t,c;
for(int i=0; i<E; i++)
{
scanf("%d%d%d",&s,&t,&c);
cost[s][t] = c;
}
return;
}
/*初始化*/
void Init()
{
for(int i=0; i<V; i++)
{
for(int j=0; j<V; j++)
{
if(i==j) d[i][j] = 0;
else if(!cost[i][j])
{
d[i][j] = INF;
}
else
d[i][j] = cost[i][j];
}
}
return;
}
/*floyd_warshall算法*/
void warshall_floyd()
{
for(int k=0; k<V; k++)
{
for(int i=0; i<V; i++)
{
for(int j=0; j<V; j++)
{
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
return;
}
int main()
{
build_map();
Init();
warshall_floyd();
for(int i=0; i<V; i++)
{
for(int j=0; j<V; j++)
{
if(d[i][j]==INF)
{
printf("%d ~ %d : ∞\n",i,j);
}else
printf("%d ~ %d : %d\n",i,j,d[i][j]);
}
}
return 0;
}
效果图: