动态规划核心思想
首先要明确,只要涉及求最值,一定是穷举所有可能,然后比较得出最值,穷举可以是回溯(暴力穷举)、递归等,满足重叠子问题等特征时,可以优化穷举的效率,也就是动态规划
- 动态规划本质就是将问题拆分为子问题+穷举,但不是暴力穷举,其思想源于暴力穷举,但使用了“备忘录”或DP Table进行优化,从而利用子问题解决大问题,此外再无奥妙可言(思考如何穷举->追求聪明地穷举)
- 动态规划一般都涉及表格,就是用一个表记录所有已解决子问题的解,并在此后尽可能的利用这些子问题的解,进而解决总问题
- 动态规划三要素:重叠子问题、最优子结构、状态转移方程
找状态转移方程的方法
找状态转移方程,核心问题就是状态、选择、DP数组的定义
- 思考问题有什么“状态”,即算法推进过程中会改变的变量
一般有几个状态量,dp数组就需要几个维度 - 思考对于每个状态,有什么“选择”使状态改变
ps. 技巧:类比数学归纳思想,假定已知dp[0]...dp[i-1]
,问自己怎么通过这些结果算出dp[i]
,最后将这个模式套入所有dp的推导即可 - 思考如何用DP数组/函数的含义来表现“状态”和“选择”
一般DP数组的下标(或DP函数的参数)就是2中的“状态”,DP数组保存的变量就是要优化的指标 - 思考问题的base case(最简单情况)是什么
一般而言,动态规划就是将某个指标最大化,这个值的最优,由子问题的最优来实现,子问题通常就是单元格的值,而不断进行“转移”的状态就是单元格的坐标,由此,求解每个单元格的子问题,最终求解整个问题
进一步优化:状态压缩
- 进一步的,写出完整动态规划解法后,如果dp表中各单元格的计算只依赖相邻的单元格,还可以进行状态压缩(仅用于降低空间复杂度,但可读性大大降低)
例如,二维dp表,每行只依赖上一行,就可以压缩为一维dp表
甚至每格依赖左上、上、左的情况,也可以压缩
DP问题一般求解步骤
技巧:
- 动态规划中,需要将某个指标最大化
- 这个要优化的值,通常就是单元格的值
- 由此确定子问题,找出DP Table网格的坐标轴
套路:遇到求最值问题,先思考最优子结构(用子问题解决大问题),写出状态转移方程,就对应暴力解,从暴力解就可看出有无重叠子问题,如果有就用dp数组优化
步骤:
- 找出“状态”和“选择”
- 明确dp数组/函数的定义
- 寻找“状态”间的关系(找状态转移方程)
技巧:找状态转移方程需要归纳思维,类比数学归纳思想,假定已知dp[0]...dp[i-1]
,问自己怎么通过这些结果算出dp[i]
,最后将这个模式套入所有dp的推导即可
技巧:先找出base case和最终答案位置,根据相对位置关系猜测状态转移方程
如果这步无法完成,可能是由于dp数组含义定义不恰当,需要重新定义;也可能时dp给出的信息不够,不足以推出下一步,需要将dp扩大为二维/三维数组