暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,以下图。为了节省经费以及方便计划旅程,小哼但愿在出发以前知道任意两个城市以前的最短路程。
上图中有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号顶点的状况下,任意两点之间的最短路程更新为:
经过上图咱们发现:在只经过1号顶点中转的状况下,3号顶点到2号顶点(e[3][2])、4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。
接下来继续求在只容许通过1和2号两个顶点的状况下任意两点之间的最短路程。如何作呢?咱们须要在只容许通过1号顶点时任意两点的最短路程的结果下,再判断若是通过2号顶点是否可使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为以下。
1 //通过1号顶点
2 for(i=1;i<=n;i++)
3 for(j=1;j<=n;j++)
4 if (e[i][j] > e[i][1]+e[1][j]) e[i][j]=e[i][1]+e[1][j];
5 //通过2号顶点
6 for(i=1;i<=n;i++)
7 for(j=1;j<=n;j++)
8 if (e[i][j] > e[i][2]+e[2][j]) e[i][j]=e[i][2]+e[2][j];
在只容许通过1和2号顶点的状况下,任意两点之间的最短路程更新为:
经过上图得知,在相比只容许经过1号顶点进行中转的状况下,这里容许经过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。
同理,继续在只容许通过一、2和3号顶点进行中转的状况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:
最后容许经过全部顶点做为中转,任意两点之间最终的最短路程为:
整个算法过程虽说起来很麻烦,可是代码实现却很是简单,核心代码只有五行:
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号顶点进行中转,接下来只容许通过1和2号顶点进行中转……容许通过1~n号全部顶点进行中转,求任意两点之间的最短路程。用一句话归纳就是:从i号顶点到j号顶点只通过前k号点的最短路程。
1 #include
2 int main()
3 {
4 int e[10][10],k,i,j,n,m,t1,t2,t3;
5 int inf=99999999; //用inf(infinity的缩写)存储一个咱们认为的正无穷值
6 //读入n和m,n表示顶点个数,m表示边的条数
7 scanf("%d %d",&n,&m);
8 //初始化
9 for(i=1;i<=n;i++)
10 for(j=1;j<=n;j++)
11 if(i==j) e[i][j]=0;
12 else e[i][j]=inf;
13 //读入边
14 for(i=1;i<=m;i++)
15 {
16 scanf("%d %d %d",&t1,&t2,&t3);
17 e[t1][t2]=t3;
18 }
19 //Floyd-Warshall算法核心语句
20 for(k=1;k<=n;k++)
21 for(i=1;i<=n;i++)
22 for(j=1;j<=n;j++)
23 if(e[i][j]>e[i][k]+e[k][j] )
24 e[i][j]=e[i][k]+e[k][j];
25 //输出最终的结果
26 for(i=1;i<=n;i++)
27 {
28 for(j=1;j<=n;j++)
29 {
30 printf("%10d",e[i][j]);
31 }
32 printf("\n");
33 }
34 return 0;
35 }
另外须要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图,由于带有“负权回路”的图没有最短路。例以下面这个图就不存在1号顶点到3号顶点的最短路径。由于1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减小1,永远找不到最短路。其实若是一个图中带有“负权回路”那么这个图则没有最短路。