1 | 核心概念:最优子结构、状态转移方程、边界、重叠子问题 |
举例: leetcode53-最大子数组和
输入:[-2,1,-3,4,-1,2,1,-5,4]
输出:6
思路
用 f(i) 代表以第 i 个数结尾的「连续子数组的最大和」.
这里为什么要限定 第i个数结尾? 因为f(i) 依赖f(i-1) 的连续.
状态转移方程:f(i) = Math.max(f(i-1) + nums[i], nums[i]). 含义:如果f(i-1)小于0,取nums[i]就够了.
怎么根据以上获取需求的解?
最大子数组的和 其实就是最大的f(i). 所以 maxV = Math.max(maxV, f(i)).
1 | public static int maxSubArray(int[] nums) { |
动态规划
解题核心
- 第一步:状态的定义
- 第二步:状态转移方程的定义
最优子结构
能将大问题拆成几个小问题,且满足无后效性、最优子结构性质。
大问题的最优解可以由小问题的最优解推出,这个性质叫做“最优子结构性质”。
回顾我们对f(n)的定义:我们记“凑出n所需的最少钞票数量”为f(n).
f(n)的定义就已经蕴含了“最优”。利用w=14,10,4的最优解,我们即可算出w=15的最优解。
无后效性
一旦f(n)确定,“我们如何凑出f(n)”就再也用不着了。
要求出f(15),只需要知道f(14),f(10),f(4)的值,而f(14),f(10),f(4)是如何算出来的,对之后的问题没有影响。
“未来与过去无关”,这就是无后效性。
(严格定义:如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响。)
常见问题
- 青蛙跳阶问题
- 凑零钱问题
- 最长递增子序列
- 最小编辑距离
- 背包问题
将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解
动态规划实际上是一类题目的总称,并不是指某个固定的算法
什么情况下考虑使用动态规划呢?
- 如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。
动态规划、分治法、贪心
- 动态规划实际上是一类题目的总称,并不是指某个固定的算法。
- 动态规划的意义就是通过采用递推(或者分而治之)的策略,通过解决大问题的子问题从而解决整体的做法。
- 动态规划的核心思想是巧妙的将问题拆分成多个子问题,通过计算子问题而得到整体问题的解。而子问题又可以拆分成更多的子问题,从而用类似递推迭代的方法解决要求的问题。
与分治法最大的差别是:适合于用动态规划法求解的问题,
经分解后得到的子问题往往不是互相独立的
(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)
分治法
子问题是相互独立的.
贪心
- 有效的括号字符串
- 跳跃游戏
- 是寻找最优解问题的常用方法
- 先找到【局部最优】, 并希望最后【堆叠出的结果】也是最好/最优的解
- 好像一个贪婪的人,贪图眼前局部的利益最大化。看不到长远的东西
解题步骤
1 | 从某个初始解出发 |