1. 题目
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
2. 解题思路
这题一看要么用动态规划,要么用贪婪。通过举例子来看规律,
| 1 | 3 | 1 |
|---|---|---|
| 1 | 5 | 1 |
| 14 | 2 | 1 |
很明显,礼物最大值的路径为:1 -> 1 -> 14 -> 2 -> 1。所以这个题肯定不能用贪婪,就用动态规划了。
那么状态方程是什么呢?随便一个点,到这个点的礼物的价值为,它上一个或左一个点的价值中最大的那个,加上当前点的价值,公式表示为:f(m, n) = max(f(m-1, n), f(m, n-1)) + arr[i][j]
需要注意的是,records并没有用一个二维数组记录每个点的最大礼物价值,而是使用了一个长度为n的一维数组来记录。节省了不少空间。
3. 代码实现
3.1 动态规划
class Solution:
def maxValue(self, grid: List[List[int]]) -> int:
"""
动态规划:
状态转移方程
f(m, n) = max(f(m-1, n), f(m, n-1)) + a[m][n]
"""
if not grid or not grid[0]: return 0
# 初始值
m, n = len(grid), len(grid[0]) # 行数和列数
records = [0 for i in range(n)] # 记录之前的礼物价值,记录每一个位置的价值肯定可以,但是可以使用更少的空间吗,当然可以长度为n即可
i = 0
while i < m:
j = 0
while j < n: # 计算当前位置的礼物最大价值, 不断更新records中的礼物价值,
if j == 0:
records[j] += grid[i][j]
else:
records[j] = max(records[j], records[j-1]) + grid[i][j]
j += 1
i += 1
return records[-1]
直接将礼物价值保存在原矩阵中
class Solution:
def maxValue(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
for j in range(1, n): # 初始化第一行
grid[0][j] += grid[0][j - 1]
for i in range(1, m): # 初始化第一列
grid[i][0] += grid[i - 1][0]
for i in range(1, m):
for j in range(1, n):
grid[i][j] += max(grid[i][j - 1], grid[i - 1][j])
return grid[-1][-1]
作者:jyd
链接:https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/solution/mian-shi-ti-47-li-wu-de-zui-da-jie-zhi-dong-tai-gu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4. 总结
对于动态规划(dynamic programming,简称DP)问题而言,这个题应该算是比较简单的了。当然,在不知道动态规划之前,这个题肯定还是很难的。
5. 参考文献
[1] 剑指offer丛书
[2] 剑指Offer——名企面试官精讲典型编程题
本文详细解析了在m*n棋盘上寻找从左上角到右下角路径中礼物最大价值的问题,通过动态规划方法找到最优解,避免了贪婪算法的局限性。
5500

被折叠的 条评论
为什么被折叠?



