剑指 Offer 47. 礼物的最大价值(动态规划dp)

  • 题目描述
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

 

示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

这道题,第一想法就是用动态规划,也找到了转移方程,但是呢,就是实现的过程有问题,来我们看看题解大佬的清晰分析。

  • 解法:动态规划

1.为什么想到用动态规划?

从棋盘的左上角开始拿格子里的礼物,并每次 向右 或者 向下 移动一格、直到到达棋盘的右下角。根据题目说明,易得某单元格只可能从上边单元格或左边单元格到达。

 这一步是可以得到的,OK?

看,这是不是就是个动态规划典型情况?动态规划是把一个大问题拆解成一堆小问题,并且将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解。

2.动态规划具有以下性质:

  • 【无后效性】“未来与过去无关”,这就是无后效性。
  • 【最优子结构】大问题的最优解可以由小问题的最优解推出,这个性质叫做“最优子结构性质”

我们可以定义状态dp,dp(i,j)代表从棋盘左上角开始,到达单元格(i,j)时能拿到礼物的最大累计价值。主要方法是每次通过改变grid单元格元素的值,将grid矩阵当作dp矩阵。(这里参考大佬的题解)

 代码

class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if i ==0 and j == 0:
                    continue
                if i == 0:
                    grid[i][j] += grid[i][j-1]
                elif j ==0:
                    grid[i][j] += grid[i-1][j]
                else:
                    grid[i][j] += max(grid[i][j-1], grid[i-1][j])
        return grid[-1][-1]

时间复杂度O(MN):分别为两个for循环遍历的矩阵长宽

空间复杂度O(1):原地修改grid大小不用额外空间

  • 优化

优化的目的是当grid很大,i=0或者j=0的情况仅仅占少数,相当于循环每轮都冗余了一次判断。因此,可先初始化矩阵第一行和第一列,再开始遍历递推。

class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        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, len(grid)):
            for j in range(1, len(grid[0])):
                    grid[i][j] += max(grid[i][j-1], grid[i-1][j])
        return grid[-1][-1]

参考: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/

原文地址:https://www.cnblogs.com/yeshengCqupt/p/13508202.html