与@גלעד ברקן 的评论一致,解决此问题的诀窍是在递归关系中添加一个额外的维度,该维度表示到达单元格所需的左侧数。
注意:为了使这个问题更容易,我已经反转了给定矩阵的行,以便允许的移动方向是向下和向右,起始单元格变为(0, 0) 和结束单元格变为(W - 1, H - 1),其中W 和H 分别是矩阵的宽度和高度。此外,矩阵和相应的最大幸福 dp 表被认为是按行主要顺序排列的,即matrix[y][x] 是单元格(x, y) 处的值。
我们的 true 基本情况是访问起始单元格的幸福感,即matrix[0][0] 或0,取决于起始单元格的值是否必须包含在总数中幸福。但是,与实际递归相比,我们还有两个 伪 基本情况更容易计算。
这些伪基本情况是最左边的列(请记住,行已被颠倒,所以这是原始矩阵中的 最右边 列)和最上面的行。最左列单元格(0, y) (1 <= y < H) 的递归关系为:
h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in 0 to y-1)
h 是我们的最大幸福 dp 表,h[y][x][r] 是从单元格(0, 0) 到(x, y)(遵循给定规则)在连续获得r 的权利(或离开,对于原始的非行反转矩阵)就在到达(x, y)之前。因此,如果r 为0,则最后遍历的单元格是当前单元格的正上方。到达最左列中的单元格的唯一可能方法是从其正上方的单元格中获取。
关于我们的最大幸福度表需要注意的重要一点是,任何单元格(x, y) 的最大幸福度由h[y][x][r] 的所有值r 的最大值给出。如果h是一个三维数组/列表,也可以表示为max(h[y][x])。
同样,最顶行中单元格(x, 0) (1 <= x < W) 的递归关系为:
h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x
这源于这样一个事实,即到达最顶行的单元格的唯一方法是从起始单元格继续前进。到达最顶行单元格的权利数等于其从 0 开始的 X 坐标,因此惩罚为-x。
现在我们有了基本案例,我们可以继续讨论单元格(x, y) 的递归关系,其中1 <= x < W 和1 <= y < H。
在建立这种递归关系时,首先要注意的是只有两种可能性:最后一个单元格在给定单元格的上方或在给定单元格的左侧 .我们将考虑这两种可能性。
如果最后一个单元格高于给定单元格,则它的坐标为(x, j),其中0 <= j < y。为了获得最大的幸福,最后一个单元的幸福也必须是最大的。因此,当最后一个单元格高于给定单元格时,(x, y) 处的最大幸福由下式给出
h[y][x][0] = matrix[y][x] + max(max(h[j][x]) for j in 0 to y-1)
请注意,获得的权利数量 (r) 为 0,max(h[j][x]) 是到达具有任何权利数量的单元格(x, j) 时获得的最大幸福感。
如果最后一个单元格在给定单元格的左边(或者换句话说,最后一次移动在右边),它的坐标就是(x - 1, y)。这是因为我们一次只能向右移动一个单元格。与前一种情况不同,最后一个单元格的位置是恒定的。但是,到达此单元格的连续权限 (r) 数量是可变的。因此,我们必须考虑r 的所有可能值。由于可以创建最少 1 个权限和最多 x 个权限,1 <= r <= x。对于r 的每个值,该单元格的最大幸福度是r - 1 右侧的左侧单元格(最后一个单元格)的最大幸福度加上(x, y) 处的值减去惩罚(即@987654366 @)。在伪代码中,
for r in 1 to x:
h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r
我们现在拥有以自下而上或自上而下的方法运行算法所需的一切。我们的最终答案,即到达右下角(原始非行反转矩阵的左下角)单元格所需的最大幸福,由max(h[H-1][W-1]) 给出。
将所有内容放在一起,我们有以下(Python 式)自底向上算法,时间复杂度为O(W^2 * H^2) 算法:
h[0][0][0] = matrix[0][0]
for x in range(1, W):
h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x
for y in range(1, H):
h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in range(y))
for y in range(1, H):
for x in range(1, W):
for r in range(1, x + 1):
h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r
h[y][x][0] = matrix[y][x] + max(max(h[j][x]) for j in range(y))
return max(h[H-1][W-1])
仔细观察,计算max(h[j][x]) 的O(W) 操作似乎可以通过预先计算来去除,从而得到最终的时间复杂度O(W * H^2)。请注意,此优化仅对自下而上的方法有效。这是因为单元格(x, j)(其中j < y)应该已经在自下而上的方法中被访问过。在下面的代码中,max(h[y][x]) 存储在h[y][x][W] 中。
Python 3 中自底向上算法的完整优化版本(包括作为输入的给定示例矩阵和输入矩阵的行反转):
matrix = [[5, -2, -1, 5, 3, -99, 4, 0],
[5, -2, -1, -3, 3, -99, 2, -3],
[-98, -98, -98, -98, -98, -98, -98, -98],
[-99, -99, -99, -99, -99, -99, -99, -99],
[5, 0, -3, 5, -1, 7, -2, 2],
[1, 2, 7, 0, 0, 1, -1, -1]]
H = len(matrix)
W = len(matrix[0])
for row in matrix:
row.reverse()
h = [[[0] * (W + 1) for i in range(W)] for j in range(H)]
h[0][0][0] = matrix[0][0]
h[0][0][W] = h[0][0][0]
for x in range(1, W):
h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x
h[0][x][W] = h[0][x][x]
for y in range(1, H):
h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in range(y))
h[y][0][W] = h[y][0][0]
for y in range(1, H):
for x in range(1, W):
h[y][x][0] = matrix[y][x] + max(h[j][x][W] for j in range(y))
h[y][x][W] = h[y][x][0]
for r in range(1, x + 1):
h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r
h[y][x][W] = max(h[y][x][W], h[y][x][r])
print(h[H-1][W-1][W])