算法设计:双调巡游分步骤详解

双调巡游是简化的一种TSP旅行商问题,可以在n平方复杂度内解决,此问题是算法导论第二版课后习题。该问题限定了两条严格向右的路线,只在起点和终点处重合,该闭合曲线的长度接近最优,但不一定是最优,就是短。
声明:本文是我在网上找资料学习后的学习整理过程,结合了很多文章才弄懂,所以做一个学习记录。
首先看下面这张图
《算法设计:双调巡游分步骤详解》
使用动态规划的思想解决这个问题,必然要面临子问题划分和初始状态。首先确定是两条路,那么是一个二维的动态规划,设s[i][j]为当路线A走到i,且路线B走到j时的最短路径。不难看出A和B路线是对偶的,所以只需考虑一半的情况即可。即s[i][j]=s[j][i]。假设B走的永远比A快。
外层循环是A走的路线,内层循环是B走的路线,且B走的永远比A快,反应到代码里就是B的循环起点为A所在的位置。

for(int i = 0 ;i<length;i++){
	for(j=i;j<length;j++){
		//这里一会写代码
	}
}

第一次循环

当A走到0位置时,它便不再动。这时B开始移动,B要分别走完这几个点,所以B走到点1时,距离就是(0,6)和(1,0)的欧氏距离记为b1。接着B走到2点,那么需要计算(1,0)和(2,3)的欧式距离,记为b2,那么此时B一共走了b1+b2远。
同理当B走完时,b1,b1+b2,b1+b2+b3,b1+b2+b3+b4…等等就都算出来了。也就是
s [ 0 ] [ j ] ( 0 &lt; = j &lt; l e n g t h ) s[0][j](0&lt;=j&lt;length) s[0][j](0<=j<length)
都被计算出来了。

第二次循环

接着A开始走到点1了。这次需要计算的是 s [ 1 ] [ j ] ( 1 &lt; = j &lt; l e n g t h ) s[1][j](1&lt;=j&lt;length) s[1][j](1<=j<length)
B这时候从1开始走了,B走到1时,要计算的是s[1][1],s[1][1]的意思是,A走到1了,B开始从1走,那么A走到1的距离(即s[0][1])是已经确定的,只需算一下B走到1的距离。那s[1][1]和谁有管呢?在第一次的循环里,由于s[0][1]已经算出来了,此时只需要计算s[0][1]+distance[0][1],也就是s[0][1]+b1的值。
s [ 1 ] [ 1 ] = s [ 0 ] [ 1 ] + b 1 s[1][1] = s[0][1]+b1 s[1][1]=s[0][1]+b1

接着B走到点2了,这时候需要计算的是s[1][2],即A走到1了,B走到2时的最短路径。此时B有几种可能性走到2?第一种是经0走到2,第二种是经1走到2。所以这两种可能性都要考虑。
第一种:从0走到2。
说明A还是原来的A,B需要计算(0,6)(2,3)两个点的欧氏距离,即
s [ 1 ] [ 2 ] = s [ 1 ] [ 0 ] + d i s t a n c e [ ( 0 , 6 ) ( 2 , 3 ) ] s[1][2] = s[1][0] + distance[(0,6)(2,3)] s[1][2]=s[1][0]+distance[(0,6)(2,3)]
第二种:从1走到2。
即A走到点1了,B也走到点1了,然后B从点1走到点2,此时需要计算点2到点3的欧氏距离,并加上已经计算好的s[1][1],即
s [ 1 ] [ 2 ] = s [ 1 ] [ 1 ] + d i s t a n c e [ ( 1 , 0 ) , ( 2 , 3 ) ] s[1][2] = s[1][1]+distance[(1,0),(2,3)] s[1][2]=s[1][1]+distance[(1,0),(2,3)]

接着B走到了点3了。在强调一遍,现在A只是走到了点1,B要走到点3,那么点2是必须走过了,因为双调向右巡游,某个点不是属于A就是属于B,A还没走到2点,则2点此时必须由B路径已经走过(不用考虑后面循环要处理的情况)。此时我们要计算的是s[1][3],即A在1点,B在3点时的最短路径。这里就好办了,因为s[1][2]已经算出来了,s[1][3]只需要考虑B从2号点走到3号点的距离,即distance[(2,3),(5,4)]
s [ 1 ] [ 3 ] = s [ 1 ] [ 2 ] + d i s t a n c e [ ( 2 , 3 ) , ( 5 , 4 ) ] s[1][3] = s[1][2]+distance[(2,3),(5,4)] s[1][3]=s[1][2]+distance[(2,3),(5,4)]
后面的s[1][4]、s[1][5]、s[1][6]可以按这个思路依次计算出了。

这里我们就完成了
s [ 1 ] [ j ] ( 1 &lt; = j &lt; l e n g t h ) s[1][j](1&lt;=j&lt;length) s[1][j](1<=j<length)

第三次循环

A现在走到了2号点,B要从2号点开始往下走了。此时开始计算s[2][2],2号点肯定路径A或B路径由1号点过来,所以该问题等价为
s [ 2 ] [ 2 ] = s [ 2 ] [ 1 ] + d i s t a n c e [ ( 1 , 0 ) , ( 2 , 3 ) ] s[2][2]=s[2][1]+distance[(1,0),(2,3)] s[2][2]=s[2][1]+distance[(1,0),(2,3)]

s [ 2 ] [ 2 ] = s [ 1 ] [ 2 ] + d i s t a n c e [ ( 1 , 0 ) , ( 2 , 3 ) ] s[2][2]=s[1][2]+distance[(1,0),(2,3)] s[2][2]=s[1][2]+distance[(1,0),(2,3)]
然后B继续往下走,B走到3号点了,以此类推。

以上说明的是如下递推式:

1)当k < i-1时,有
d(i, k) = d(i-1, k) + |Pi-1Pi|,这里表示d(i, k)必然包含线段|Pi-1Pi| (点Pi-1和点Pi之间的距离)
2)当k = i-1时,有
d(i, k) = d(i-1, u) + |PuPi|,其中1 <= u < i-1,这里遍历u的值,寻找最短距离
3)当k = i时,有
d(i, k) = d(i-1, u) + |Pi-1Pi| + |PuPi|,其中1 <= u < i-1

代码见:https://blog.csdn.net/Mr_tianyanxiaobai/article/details/79429998

    原文作者:骑士周游问题
    原文地址: https://blog.csdn.net/bless2015/article/details/88719044
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞