暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。
上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径。这个问题这也被称为“多源最短路径”问题。
现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储。比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为∞。另外此处约定一个城市自己是到自己的也是0,例如e[1][1]为0,具体如下。
现在回到问题:如何求任意两点之间最短路径呢?通过之前的学习我们知道通过深度或广度优先搜索可以求出两点之间的最短路径。所以进行n2遍深度或广度优先搜索,即对每两个点都进行一次深度或广度优先搜索,便可以求得任意两点之间的最短路径。可是还有没有别的方法呢?
我们来想一想,根据我们以往的经验,如果要让任意两点(例如从顶点a点到顶点b)之间的路程变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不只通过一个点,而是经过两个点或者更多点中转会更短,即a->k1->k2b->或者a->k1->k2…->k->i…->b。比如上图中从4号城市到3号城市(4->3)的路程e[4][3]原本是12。如果只通过1号城市中转(4->1->3),路程将缩短为11(e[4][1]+e[1][3]=5+6=11)。其实1号城市到3号城市也可以通过2号城市中转,使得1号到3号城市的路程缩短为5(e[1][2]+e[2][3]=2+3=5)。所以如果同时经过1号和2号两个城市中转的话,从4号城市到3号城市的路程会进一步缩短为10。通过这个的例子,我们发现每个顶点都有可能使得另外两个顶点之间的路程变短。好,下面我们将这个问题一般化。
当任意两点之间不允许经过第三个点时,这些城市之间最短路程就是初始路程,如下。
假如现在只允许经过1号顶点,求任意两点之间的最短路程,应该如何求呢?只需判断e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。其中i是1~n循环,j也是1~n循环,代码实现如下。
1 for (i = 1; i <= n; i++) 2 { 3 for (j = 1; j <= n; j++) 4 { 5 if (e[i][j] > e[i][1] + e[1][j]) 6 e[i][j] = e[i][1] + e[1][j]; 7 } 8 }
但是事实上我们不仅仅只允许经过1号顶点,求任意两点之间的最短路径。
简而言之就是:从i号顶点到j号顶点只经过前k号点的最短路程。
那么关于k的这个循环我们到底是放在最外面还是放在里面呢?这个就需要我们对FLyod算法有更深一步的认识。这个算法实际上是个动态规划算法
正因为如此所以我们的 k 自然要放在最外面了!
核心代码:
1 for(k=1;k<=n;k++) 2 for(i=1;i<=n;i++) 3 for(j=1;j<=n;j++) 4 if(e[i][j]>e[i][k]+e[k][j] ) 5 e[i][j]=e[i][k]+e[k][j];
整体的代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <set> 6 #include <queue> 7 8 #define LL long long 9 using namespace std; 10 const int MAXN = 100; 11 12 int main() 13 { 14 int e[10][10],k,i,j,n,m,t1,t2,t3; 15 int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值 16 //读入n和m,n表示顶点个数,m表示边的条数 17 scanf("%d %d",&n,&m); 18 //初始化 19 for(i=1;i<=n;i++) 20 for(j=1;j<=n;j++) 21 if(i==j) e[i][j]=0; 22 else e[i][j]=inf; 23 //读入边 24 for(i=1;i<=m;i++) 25 { 26 scanf("%d %d %d",&t1,&t2,&t3); 27 e[t1][t2]=t3; 28 } 29 //Floyd-Warshall算法核心语句 30 for(k=1;k<=n;k++) 31 for(i=1;i<=n;i++) 32 for(j=1;j<=n;j++) 33 if(e[i][j]>e[i][k]+e[k][j] ) 34 e[i][j]=e[i][k]+e[k][j]; 35 //输出最终的结果 36 for(i=1;i<=n;i++) 37 { 38 for(j=1;j<=n;j++) 39 { 40 printf("%10d",e[i][j]); 41 } 42 printf(" "); 43 } 44 return 0; 45 }