任意两点间的最短路 Floyd及其本质

Posted zlhdbk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了任意两点间的最短路 Floyd及其本质相关的知识,希望对你有一定的参考价值。

我们知道在已知起点的情况下,求到其他任何一点的最短路是用dijkstra,那么在一个有向图中,我们想知道任意两点之间的最短路,我们就可以使用floyd,而且这个算法表面看起来非常的简单,就是一个三重循环,如果这个图有N个点,那么复杂度为O(|N|3),代码如下。

1 for(int k=0;k<n;k++)
2 for(int i=0;i<n;i++)
3 for(int j=0;j<n;j++)
4 d[i][j]=min(d[i][j],d[i][k]+d[k][j]);

在复杂度这么高的情况下,一般情况下如果不是板子题直接用的话肯定是会超时的,所以我们还是需要了解Floyd是怎么进行的,其实它的本质就是dp。

其实我从上面的代码中不难看出Floyd是采用状态转移的方式来更新各个点之间的距离的,而这个点就是k,即从i-j之间的最短路是否经过k点,不断的更新从而取得最优解,下面我们详细的说一下。

假设从顶点i出发,仅经由顶点Vk={1,2,3,···,k}抵达顶点j的最短路径成本为Ak[i,j]。首先,A0[i,j]表示从i到j不经由其他任何顶点,所以其值就等于连接i,j边的权值,如果不存在边时其大小为正无穷,接下来是k=1,2,3,···,|N|的情况,我们需要通过Ak-1来计算Ak,那么我们只需要考虑是否经过k点两种情况。

如果经过k,则路径会被分为i-k,k-j两个路径,而且这两个路径全都只经过Vk-1,中的顶点,因此Ak=Ak-1[i,k]+Ak-1[k,j]。

如果不经过k,那就意味着Ak[i,j]只经过i,j及属于Vk-1中的顶点,所以Ak[i,j]=Ak-1[i,j];

综上所知Ak[i,j]=min(Ak-1[i,j],Ak-1[i,k]+Ak-1[k,j])。

那么我们接下来看这个题目

洛谷P1119灾后重建

在这个题目中,图的状态是随着时间而改变的,在不同的时刻的询问下,任意两点之间的最短路也会发生改变,如果每问一次就用一次Floyd的话,毫无疑问的超时,那我们注意观察题目可以发现,某一个点能不能使用是随着时间变化的,我们在前面提到过,Floyd使用的点是根据k来变化的,及循环内部是用不到k+1及以后的点的,那么因为它的询问根据之间逐渐增加的,所以我们只要根据时间来不断的从更新k内部的点即可。

下面是完整代码

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <queue>
 6 #include <stack>
 7 #include <stdio.h>
 8 #include <cmath>
 9 #include <string.h>
10 
11 using namespace std;
12 #define ll long long
13 static const int WHITE=0;
14 static const int GRAY=1;
15 static const int BLACK=2;
16 static const int INF=(1<<20);
17 int N,M,Q;
18 int time1[205];
19 int map[205][205];
20 void floyd(int k)
21 {
22     for(int i=0;i<N;i++)
23     for(int j=0;j<N;j++)
24     map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
25     return ;
26 }
27 int main()
28 {
29     freopen("C:\Users\16599\Desktop\in.txt","r",stdin);
30     scanf("%d%d",&N,&M);
31     int now=0;
32     for(int i=0;i<N;i++)
33     {
34         int a;
35         scanf("%d",&a);
36         time1[i]=a;
37     }
38     for(int i=0;i<=N;i++)
39     for(int j=0;j<=N;j++)
40     map[i][j]=((i==j)?0:INF);
41     for(int m=0;m<M;m++)
42     {
43         int i,j,k;
44         scanf("%d%d%d",&i,&j,&k);
45         map[i][j]=map[j][i]=k;
46     }
47     scanf("%d",&Q);
48     for(int q=1;q<=Q;q++)
49     {
50         int x,y,t;
51         scanf("%d%d%d",&x,&y,&t);
52         while(time1[now]<=t&&now<=N)
53         {
54             floyd(now);
55             now++;
56         }
57         if(t<time1[x]||t<time1[y]||map[x][y]==INF)
58         printf("-1
");
59         else
60         {
61             printf("%d
",map[x][y]);
62         }
63     }
64     return 0;
65 }

 

以上是关于任意两点间的最短路 Floyd及其本质的主要内容,如果未能解决你的问题,请参考以下文章

任意两点间的最短路问题 Floyd-Warshall算法

Floyd(动态规划)求解任意两点间的最短路径(图解)

任意两点间的最短路问题(Floyd-Warshall算法)

灾后重建(对Floyd的认识)

AOJ GRL_1_C: All Pairs Shortest Path (Floyd-Warshall算法求任意两点间的最短路径)(Bellman-Ford算法判断负圈)(代码

最短路(Floyd算法的动态规划本质)- HDU 2544