任意两点间的最短路 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])。
那么我们接下来看这个题目
在这个题目中,图的状态是随着时间而改变的,在不同的时刻的询问下,任意两点之间的最短路也会发生改变,如果每问一次就用一次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及其本质的主要内容,如果未能解决你的问题,请参考以下文章
AOJ GRL_1_C: All Pairs Shortest Path (Floyd-Warshall算法求任意两点间的最短路径)(Bellman-Ford算法判断负圈)(代码