题意:有n个路口,和m条小路,连接两个路口,每条路口都有各自要话费的时间,求从第一个路口到最后一个路口花费的最少时间 |
写这道题的时候并没有掌握最短路径算法,这道题最后也没能AC。不过也借此机会学一下这类的方法,先从floyd算法开始吧。
该算法解题思路:
1.任意两个路口之间,可能的情况无非是直接一步过去,要么就中间经过若干路口间接到达。那么如果i路口和j路口之间的若有一个中转路口k,i可以先到k再到j。则两点之间的最短时间便是
dp[i][j] = min(dp[i][k]+dp[k][j],dp[i][j])。
然后外层循环遍历k,内层遍历两两端点(路口)即可。
.2. 如果这时候k-j的过程中又有可以中转的路口,而且往后还有好几个类似的呢?
这里来个例子讲,以下两两是联通两路口的小路。
5 6
6 7
7 8
8 10
比如我在k=6,要从5到10的时候,也就是拿6做中转,理想上我本应该是dp[5][6]+dp[6][10],应该直接就联通了,但是注意到这里dp[6][10]是没有路的,后面的dp[6][7],dp[7][8]都还没有遍历到,所以在k等于6的时候,dp[5][10]还不能说由5到6再通过6到7、8到10 。
那可是最后就是要dp[5][10]是由5->6->7->8->10的路径联通的呀,怎么实现的呢?
看循环的后续。
当前k=6时,会更新dp[5][7]=dp[5][6]+dp[6][7]。
当k=7时,会更新dp[5][8] = dp[5][7] + dp[7][8]。注意这里的dp[5][7]就是上一步的处理结果。
当k=8时,会更新dp[5][10] = dp[5][8] + dp[8][10]。完成上述的设想。
也就是说两端点间的最小距离会随着中转站的更新而改变/更新。
这也就是floyd算法的精髓了。
还有一种情况是往回折返的,其实就是遍历到低位中转站的时候连接到了高位的端点了,具体如下:
总体时间复杂度O(n三次方),但是数据规模小可以过。算法本身的思想很棒
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <limits.h>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)&&(n&&m))
{
vector<vector<int> > dp(101,vector<int>(101,1e7));
for(int i=1;i<=m;i++)
{
int x,y,time;
cin>>x>>y>>time;
dp[x][y] = dp[y][x] = time;
}
for(int i=1;i<=n;i++)
dp[i][i] = 0;
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
cout<<dp[1][n]<<endl;
}
return 0;
}