(1) 单向取数问题
【问题描述】一个 m×n 的方格,每个格子都有一个数字。现在从方格的左上角出发,到右下角停止。要求只能往右走或往下走,且一次只能走一步。现在使经过的所有数字的和最大,问最大值是多少?(1≤m,n≤1000)
【分析】
很容易想到贪心算法,即每次往数值更大的方向走。不过它是错误的。正确的做法是动态规划:
1. 划分阶段:每走一步为一个阶段。
2. 状态表示:f(r,c)表示从起点出发走到第 r 行第 c 列②时经过数字总和的最大值。
3. 状态转移方程
边界处理:让方格的下标从 1 开始,这样方格就可以多出一圈——0。
(2) 变式问题
① 必须经过某点
解决方法:将取数过程分成两部分。第一部分的起点是(1,1),终点是这个点;第二部分的起点是这个点,终点是(m,n)(第 m 行第 n 列)。比如,要求必须过(3,2),就可以按下图把问题一分为二:
(e……就当这两张图是连在一起的……)
② 不能经过某点
一个简单的办法是:把那个点的值设置成-INF(负无穷大)。由于经过此点要“付出巨大的代价”,所以状态不会从这点转移而来。
③ 竖直方向上最多有连续两个点相邻
解决方法:在状态中加一维,表示“已经连续向下走的次数”。
1. 状态表示:f(r,c,i)表示从起点出发走到第 r 行第 c 列时经过数字总和的最大值,i 表示“已经连续向下走的次数”。
2. 状态转移方程
3. 输出:f(m,n,0)和 f(m,n,1)中的最大值。
④ 传送门#1
到达点(a,b)后,便立刻跳转到(a',b'),其中 b'>b(否则会发生无限传送)。
解决方法:分两种情况讨论,即经过传送门、不经过传送门。
如下图,传送门的入口为(4,3),出口为(2,5),则整个过程可以分为两个子问题。
⑤ 传送门#2
到达点(a,b)后,便立刻跳转到(a',b'),反过来也能跳转,其中 a<a',b'>b(否则会发生无限传送)。
解决方法:分三种情况讨论,即不经过传送门、先经过左侧的传送门、先经过右侧的传送门。
(3) 传纸条①
【问题简述】一个 m×n 的方格,每个格子都有一个数字,但左上角和右下角都是 0。现在从方格的左上角引出两条路径,它们同时出发,都到右下角停止②。要求只能往右走或往下走,一次只能走一步,且两条路径不能交叉、重合。现在使经过的所有数字的和最大,问最大值是多少?(1≤m,n≤50)
【分析】
如果还使用(1)的动态转移方程,就很有可能在第一条路径选择完成后,第二条路径无法到达终点!所以要同时考虑两条路径。
思路 1:
1. 划分阶段:每走一步为一个阶段(一次只移动一张纸条)。
2. 状态表示:设两张纸条分别位于第 r1行第 c1列、第 r2 行第 c2 列,那么 f(r1,c1,r2,c2)表示两张纸条从起点出发,分别到达这两个点时经过的数字和的最大值。
为了保证道路不交叉,应有 x1<r2。
3. 状态转移方程:
4. 一个优化:因为 r1+c1=r2+c2,所以可以去掉一维。这样时间可由四次方降到三次方。
5. 输出:f(m, n-1, m-1, n)
思路 2:
可以发现,只要知道移动步数和两个横坐标,那么两个纵坐标就可以计算出来。
1. 划分阶段:每走一步为一个阶段。
与思路 1 不同的是,这一次两张纸条要同时移动。
2. 状态表示:设两张纸条分别位于第 r1行第 c1列、第 r2 行第 c2列,i 为移动步数,那么 f(i,r1,r2)表示两张纸条从起点出发,经过 i 步分别到达这两个点时经过的数字和的最大值。
为了保证道路不交叉,应有 r1>r2。
3. 状态转移方程:
c1=i+2-r1,c2=i+2-r2,同时 r1+c1=r2+c2。
4. 输出:f(m+n-3, m, m-1)
m+n-3 是从左上角出发,到右下角的移动步数。