最近LeetCode的动态规划21天入门系列的学习计划,经历了一个从自闭到自闭的过程,但不管怎么说,还是及时做个笔记总结一下吧,「动态规划」 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台
首先讲讲自己做动态规划题的感悟。感觉动态规划的题就是有点抽象,有一些比较直观的,没接触过也能大致推导出转移方程,不直观的那种基本上只能记住套路,如果你想试图证明其合理性也不是不行,就是非常非常难。而且找状态的时候是需要经验的,有时候需要猜,设置的状态到底合不合理,遍历顺序是否是对的等等,感觉差不多了,找几组数据测试,如果是对的那说明基本是没问题了。
然后关于动态规划的学习心得可以看看:题目求助|求助:动态规划怎么学 - 力扣(LeetCode)
其中weiwei大佬的分享相当棒,我把他的图贴出来了:
总结一下大佬的话,就是动态规划这少钻牛角尖吧,毕竟我们是写代码的不是搞数学的,在面对动态规划经验比证明更重要。与其在一道题上钻太多牛角尖不如多看几道题的结题流程和思路,当我们的脑子有足够的记忆支撑后,一切都将变得不再抽象了。连大佬都感觉难了,我们这种渣渣就好好放心吧哈哈哈。
接下来讲正事了,主要参考视频来自:https://www.bilibili.com/video/BV13Q4y197Wg。
个人按照动态规划入门系列进行分类,主要分为:
斐波那契数列系列、容易推导出转移方程的题、容易看出用贪心的题、最大序列问题、打家劫舍系列、股票系列、完全背包系列、子序列问题、字符串问题。然后动态规划一般是自下而上,简单的说即从1到n,而递归一般是从上到下,即从n到1。
首先,动态规划是找到状态和状态转移方程,然后用初始状态一步一步转移最终得到你要的答案,其一般分为五步:
- 确定dp数组以及下标的含义(寻找状态)
- 确定递推公式(状态转移方程)
- dp数组如何初始化(边界条件)
- 确定遍历顺序
- 举例推导dp数组(验证并修改)
一、斐波那契数列系列
这个系列算是比较简单好理解的,拿来入门练练手还是蛮不错的。主要有下面5个题目。
746. 使用最小花费爬楼梯:转移条件变复杂了,复杂在判断上
91. 解码方法:这个也是复杂在判断上,同意也是可以一次一个字母或者两个字母,
二、容易推导出转移方程的题
这种题目也算是比较好上手入门理解的了,即使你没做过,但是仔细思考思考可以比较容易地发现其中的规律。因为这几种的dp数组下标的含义在题目中提示的比较明显了,相对直白。
这个还是稍微有点难度,要知道从二叉搜索树的性质,并从根节点入手
这题难在遍历顺序的理解,从底往上便利更好理解。本质在于找到每一行的最小累加值。
三、容易看出用贪心的题
这题的难点在于要看出数组的下标加上数组元素值即为当前点所能到达的最大长度,
55.跳跃游戏是步步贪,45. 跳跃游戏 II是区间贪。区间贪需要多一个循环遍历区间,在区间中找到最优值。
四、最大序列问题
这两种是单股状态的,还算能理解其中的含义。但是918. 环形子数组的最大和:这里有一个不好发现的规律,就是子数组的最大和出现在头尾拼接的地方的话,那么其值为整个数组和减去最小子数组和,而最小子数组和一定不出现在头尾拼接处(邮电抽象,要自己画个图理解理解,这个即官方题解中说的Kanade算法)。
这两种是两股状态互相影响的,开始比较抽象起来了。当然,这两种都可以不要动态规划做,用找规律方法,即从前到后遍历子序列以及从后到前遍历子序列直至遇到第一个负数。
这两个题都一样,需要设置正负两种状态,二者根据元素正负状态值互相转移。
五、打家劫舍系列
本质上还是斐波那契系列的延伸,但是因为名字比较有趣,就单独拿出来吧。这类题也还算好,能比较直观地理解转移方程的原理,即步步覆盖性。
关于打家劫舍,我感觉设置成当天偷或者不偷两种状态会比只设置到当天所能挣到的最大值就这一种状态来的更好理解。下面的打家劫舍3就是分为偷和不偷两种状态的
这个跟上面的有点不太像,上面两个是线性dp,这个变成树形dp了
740. 删除并获得点数有点难了,需要做比较复杂的转换,有一定难度,要用到排序加动态规划。
六、股票系列
股票系列可参考:力扣https://leetcode-cn.com/circle/article/qiAgHn/
这个转化一下就变成跟121. 买卖股票的最佳时机一样了。
股票系列是经典的多状态耦合的线性dp的题目。精髓在于捋清状态,这个太经典了,网上有好多大佬的讲解。
七、完全背包系列
(背包即容量或target,金币为数组元素)完全背包系列问题也是经典呀。其有两个精髓,一是拿价格来当状态并且把金币放进dp数组的下标当中进行运算;二是在于先遍历金币还是先遍历背包,先遍历背包计算的是排列数,先遍历金币计算的是组合数。从个人角度来说,先先遍历背包会比先遍历金币来的更好理解一些。
八、子序列问题
这种子序列问题通常可以通过画图或画表找到规律
这个用贪心画个折线图来做会简单许多。
后面三个才算是典型的子序列问题是,需要画一画二维矩阵找出转移规律。
九、字符串问题
这几个都是典型的区间dp问题。
这里的区间dp用到了两个下标i和j,是一个二维的dp方程,需要不断更新i、j遍历所有可能的区间,时间复杂度为o(n2)
十、分不出系列的
这种一般都是数学题了,或是用其他如双指针等办法即可解决的问题
这是一个数学题
这个用双指针感觉比直接用动态规划快多了
4)和5)都是用的矩阵前缀和思想,关于矩阵前缀和负雪明烛大佬的图解题解讲的相当不错,可参考:如何求二维的前缀和,以及用前缀和求子矩形的面积
本质上是一道数学找规律题。知道规律后能很容易理解状态和转移方程。可参考题解:
当然,这只是动态规划入门系列的总结,后面还有更复杂的树形dp等等,可参考: