动态规划通用解法总结

背景: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遇到动态规划的题目不再会毫无头绪啦~~~

相关文章
相关标签/搜索