動態規劃通用解法總結

背景:leetcode刷題遇到動態規劃的題目,作不出來時看別人的code,也能夠理解,但仍是沒有找到create solution的技巧,單純的comprehend and remeber,直到遇到了下面這篇題解,終於造成了本身的動態規劃通用解題方法,拿全部easy難度的題目試了下,結果橫掃java

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108870/Most-consistent-ways-of-dealing-with-the-series-of-stock-problemspython

做者:labuladong
連接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。數組

 

在動態規劃題目中,變形數量最多的差很少就是股票問題了,上述解法也圍繞股票問題展開網絡

 

根據上述帖子,我總結了動態規劃的解題方法以下優化

 

Question1:如何識別題目適用於動態規劃?spa

某一過程包含多種狀態(狀況),後一種狀態的生成依賴於前面的狀況code

 

Quetion2:如何寫動態規劃?blog

一、找出問題中的狀態(state)和選擇(choice)索引

二、用選擇去表達狀態之間的轉移內存

like below

for state1 in state1_list:
    for state2 in state2_list:
        for state3 in state3_list:
            dp[state1][state2][state3] = choose(choice1, choice2, choice3)

其中全部的choise都用前面已計算出的狀態來表示

 

Question3:如何優化動態規劃?

在上述解題方案中,常常會有沒必要要的空間複雜度消耗,咱們能夠發現並經過適用常數個變量的方式替代適用數組或者矩陣。

 

Example:

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有必定的現金,影響你偷竊的惟一制約因素就是相鄰的房屋裝有相互連通的防盜系統,若是兩間相鄰的房屋在同一夜被小偷闖入,系統會自動報警。

給定一個表明每一個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的狀況下,可以偷竊到的最高金額。

來源:力扣(LeetCode)
連接:https://leetcode-cn.com/problems/house-robber
著做權歸領釦網絡全部。商業轉載請聯繫官方受權,非商業轉載請註明出處。

class Solution:
    """步驟一:判斷是否爲動態規劃題目;很明顯,下一家的偷法依賴於前面的偷法,不一樣的偷法致使不一樣的偷盜總額,屬於動態規劃"""
    def rob(self, nums: List[int]) -> int:
        if not nums or len(nums)==0:
            return 0
        #步驟二:找到狀態和選擇;這裏狀態爲當前偷到了第幾家,用i表示,選擇爲偷或者不偷
        dp = {}
        #初始化開始狀況,當偷第-1家時,能夠理解爲偷得金額爲0
        dp[-1] = 0
        dp[0] = nums[0]
        #遍歷的全部狀況
        for i in range(1,len(nums)):
            #這裏的選擇方法爲取最大值,不一樣的選擇分別表明偷與不偷
            dp[i] = max(dp[i-2]+nums[i],dp[i-1])
        return dp[len(nums)-1]   

步驟三:優化空間複雜度;上述解法用到了長爲N的輔助列表,空間複雜度爲O(n)。優化思路爲用幾個變量替代輔助列表

能夠看到當前循環的dp[i],

依賴於當前循環dp[i-1]與dp[i-2],

即依賴於上一循環的dp[i]與dp[i-1],

因此咱們用2個變量便可解決該問題,分別cur與pre便可

代碼可優化爲

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums or len(nums)==0:
            return 0
        pre = 0
        cur = nums[0]
        for i in range(1,len(nums)):
            tmp = cur
            cur = max(pre+nums[i],cur)
            pre = tmp
        return cur

 

另外,講一下python實現動態規劃過程當中須要注意的一點,

與java不一樣,java能夠直接int[][]初始化多重數組並賦予默認值,而後直接用索引取訪問對應元素便可,但python的list結構不會初始化大小,須要咱們本身初始化,不然直接使用索引會索引越界

#初始化長度爲10的一維數組並賦予默認值0
dp = [0]*10

#初始化10*3的二維數組並賦予默認值0
dp = [[0]*10 for _ in range(10)]

但問題是,某些狀況下須要咱們去思考默認值賦予什麼才比較合適,這樣會形成困擾,咱們更但願不用去思考初始化爲什麼值(keep None便可),以下

#初始化字典(哈希表)代替一維數組(list)
dp = {}

#初始化10個字典並放到一維數組中
dp = [{}  for _ in range(10)]

這樣就解決了不給出默認值用於內存分配致使的角標越界問題

 

概括出上述套路以後,leetcode遇到動態規劃的題目不再會毫無頭緒啦~~~

相關文章
相關標籤/搜索