曼哈顿游客问题 (Manhattan Tourist Problem)
问题背景
设想有个观光团在纽约曼哈顿区,并决定从位于第59街与第8大道交叉处步行至第42街与莱克星顿大道交叉处。途中会有许多景点,游客想游览尽可能多的景点。在每个街道的交叉口游客只能向东或向南步行。即使如此,他们也有很多路线可供选择。
问题描述
在加权网格中选好一条最长的路线.
输入: 有源点和终点的加权网格G.
输出: G中从源点到终点的一条最长的路线.
设计、分析并实现基于动态规划技术的曼哈顿游客问题求解算法.
示例数据
思路
首先,设A[i,j]为到达该点的距离,i表示从上方到达该点的距离,j表示从左边到达该点的距离;设B[i,j]存储在矩阵中前进的符号,←表示该点结果是从左边那点到这点计算得出的,即从左边到该点距离最长,↑表示该点结果是从上面那点到这点计算得出的,即从上面到该点距离最长;设C[i,j]为从起始点到该点的最短距离。
解决动态规划问题的两个条件是优化子结构和重叠子问题。这两个条件曼哈顿问题都符合。
到达某一点的距离等于,到达该点上面那一点的距离加上上面那一点距离到这一点距离,和到达该点左边那一点的距离加上左边那一点距离到这一点距离的最大值,得证曼哈顿问题存在优化子结构。
每计算出一个矩阵C[i,j]中的元素,可以重复利用它来计算它右边的点和下面的点,得证重叠子问题。
根据以上证明,我们可以抽象出以下递推公式:
C[i,j]=0,if i=0 and j=0; C[i,j]=C[i,j-1]+a_j ,if i=0 and j!=0;
C[i,j]=C[i-1,j]+a_i ,if i!=0 and j=0;
C[i,j]=MAX{C[i,j-1]+a_j,C[i-1,j]+a_i },if i!=0 and j!=0;
在构造完矩阵B[i,j],C[i,j]之后,可以使用递归的方式构造优化解,从B[m,n]开始搜索,按照箭头所指的方向去搜索。如果箭头是←,则表示到达该点需要向右走;如果箭头是↑,则表示到达该点需要向下走。
如果从左边到达该点和从右边到达该点距离相等,则默认可以从上面到达该点。
伪代码
算法输入:从上面或左边到点(i,j)的距离矩阵A[i,j]
算法输出:点(i,j)的结果来源方向矩阵B[i,j]
到点(i,j)的最长距离矩阵C[i,j]
Construct-MTP(A[i,j])
C[0,0]←0;
For x←1 To i Do
C[x,0]←C[x-1,0]+a_x;
B[x,0]←"←";
For y←1 To j Do
C[0,y]←C[0,y-1]+a_y;
B[0,y]←"↑";
For x←1 To i Do
For y←1 To j Do
If C[x,y-1]+a_y ≥ C[x-1,y]+a_x
Then C[x,y]←C[x,y-1]+a_y;
B[x,y]←"↑";
Else If C[x,y-1]+a_y< C[x-1,y]+a_x
Then C[x,y]←C[x-1,y]+a_x;
B[x,y]←"←";
Return B and C;
Print-MTP(B,i,j)
If i=0 and j=0
Then Return;
If B[i,j]=="←"
Then Print-MTP(B,i-1,j);
Print "A[i,j]";
If B[i,j]=="↑"
Then Print-MTP(B,i,j-1);
Print "A[i,j]";
结果
以下图片表示从点(0,0)开始依次经过以下顺序的点到达终点(4,4)。总共经过9个点,8段路程,所经过的路径的权值总和是所有路径中最大的。
结果分析
矩阵A[i,j]为输入的测试数据,数值如下所示。这里要注意,行是j,列是i。
矩阵B[i,j],C[i,j]构造后如下所示
从点(0,0)到点(4,4)的路线如下图所示
设矩阵有n列,m行,根据递推公式和伪代码,可以推出函数Construct-MTP(A[i,j])的时间复杂度为T(n)=θ(n)+θ(m)+θ(mn)=θ(mn)。函数Print-MTP(B,i,j)的时间复杂度是T(n)=θ(n+m)。二者总体时间复杂性是T(n)=θ(mn)。
由于算法使用了三个二维数组来存储数据,所以,算法的空间复杂度是T(n)=θ(mn)+θ(mn)+θ(mn)=θ(mn)。