1. 程式人生 > 實用技巧 >劍指 Offer 47. 禮物的最大價值(動態規劃dp)

劍指 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/