用Python征服动态规划——解题攻略《leetcode-礼物的最大价值》
2024-02-19 03:04:30
在游戏中,你遇到了一个有趣的挑战:在一个 m*n 的棋盘上,每个格子都放着诱人的礼物,你需要找到一条从左上角到右下角的路径,使得你收集到的礼物价值最大化。规则很简单,你只能向下或向右移动,不能走回头路。
面对这样的问题,我们该如何找到最佳路径呢?其实,我们可以利用动态规划的思想来解决它。动态规划的核心在于将一个复杂的问题分解成一系列更小的子问题,然后逐个解决这些子问题,最终得到整个问题的答案。
具体来说,我们可以创建一个二维数组 dp,其中 dp[i][j] 表示从左上角到达格子 (i, j) 的路径上能够获得的最大礼物价值。
首先,我们需要初始化 dp 数组。由于起点是左上角的格子 (0, 0),所以 dp[0][0] 就等于棋盘上左上角格子的礼物价值。
接下来,我们开始逐行逐列地计算 dp 数组的值。对于每个格子 (i, j),它只能从上方格子 (i-1, j) 或左方格子 (i, j-1) 到达。因此,dp[i][j] 的值就等于 max(dp[i-1][j], dp[i][j-1]) 加上格子 (i, j) 本身的礼物价值。
当我们计算完整个 dp 数组后,dp[m-1][n-1] 就是从左上角到达右下角的路径上能够获得的最大礼物价值,也就是我们最终想要的结果。
为了更好地理解这个算法,我们来看一个具体的例子。假设棋盘如下:
1 3 1
1 5 1
4 2 1
那么,dp 数组的计算过程如下:
dp[0][0] = 1
dp[0][1] = 1 + 3 = 4
dp[0][2] = 4 + 1 = 5
dp[1][0] = 1 + 1 = 2
dp[1][1] = max(4, 2) + 5 = 9
dp[1][2] = max(5, 9) + 1 = 10
dp[2][0] = 2 + 4 = 6
dp[2][1] = max(9, 6) + 2 = 11
dp[2][2] = max(10, 11) + 1 = 12
最终,dp[2][2] 的值为 12,这就是从左上角到达右下角的路径上能够获得的最大礼物价值。
下面,我们用 Python 代码来实现这个算法:
def max_gift_value(grid):
"""
计算从左上角到右下角的路径上的最大礼物价值。
Args:
grid: 一个 m*n 的棋盘,每个格子都放有一个礼物。
Returns:
从左上角到右下角的路径上的最大礼物价值。
"""
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
dp[0][0] = grid[0][0]
for i in range(1, m):
dp[i][0] = dp[i-1][0] + grid[i][0]
for j in range(1, n):
dp[0][j] = dp[0][j-1] + grid[0][j]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[m-1][n-1]
# 测试用例
grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]]
max_value = max_gift_value(grid)
print(max_value) # 输出: 12
通过这段代码,我们可以轻松地计算出最大礼物价值。
常见问题解答
1. 动态规划算法的适用范围是什么?
动态规划算法适用于具有以下特点的问题:
- 问题可以分解成更小的子问题。
- 子问题的解可以被重复利用。
- 问题的最优解可以通过子问题的最优解组合得到。
2. 如何判断一个问题是否适合使用动态规划算法?
如果一个问题满足上述三个特点,那么它就可能适合使用动态规划算法。
3. 动态规划算法的时间复杂度是多少?
动态规划算法的时间复杂度通常与状态的数量和状态转移的次数有关。在本例中,状态的数量是 mn,状态转移的次数也是 mn,所以时间复杂度是 O(m*n)。
4. 动态规划算法的空间复杂度是多少?
动态规划算法的空间复杂度通常与状态的数量有关。在本例中,状态的数量是 mn,所以空间复杂度是 O(mn)。
5. 如何优化动态规划算法的空间复杂度?
在一些情况下,我们可以通过滚动数组或状态压缩等技术来优化动态规划算法的空间复杂度。例如,在本例中,我们可以只使用一个一维数组来存储 dp 值,从而将空间复杂度降低到 O(n)。
希望这篇文章能够帮助你理解动态规划算法,并学会如何使用它来解决实际问题。