劍指 Offer 47. 禮物的最大價值(動態規劃dp)
阿新 • • 發佈:2020-08-15
- 題目描述
在一個 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]