原理
使用场景
一个模型:多阶段决策最优解模型,三个特征:最优解子结构,无后效性,重复子为题。
- 多阶段决策最优解模型:即一个问题分为多个阶段,每个阶段的决策对应一组状态,我们根据这些状态寻找一组决策序列,最中获取决策序列的最优解。
- 三个特征:
- 最优解子结构:问题的最优解,包含子问题的最优解。也就是能通过子问题最优解找到问题的最优解
- 无后效性:后续的问题解决方案只依赖于前一个问题的状态,而不关心他是如何推导出来的。
- 重复子问题:不同的决策序列到达相同阶段会产生重复的状态。
空间、时间复杂度
能用动态规划解决的问题往往都是能通过回溯算法解决的,只是回溯算法的时间复杂度往往很高是指数级的O(2^n)。用动态规划这种算法往往能很大的降低是复杂度具体会变为O(nw)
空间复杂度:冬天规划因为借住了一个2维数组states[n][w+1],所以空间复杂度是O(nw)。实际上动态规划是拿时间换空间的一个思想。
几种算法模型的区别
- 分治:不能抽象成多阶段决策模型,而是将一个模型分成不同的部分一次解决。
- 贪心:是动态规划的一个特殊方法,通过局部最优解推导出全局最优解,解决问题更加高效,但是也更加受限,最优子结构,无后效性,贪心选择。
- 回溯:能用贪心、动态规划算法解决的问题几乎都能用回溯算法解决,主要是用递归方法解决问题,通过穷举所有的可能,在经过对比获取最优解,由于复杂度是指数级,试用于小数据量
- 动态规划:上述所属多阶段决策最优解模型,有重复的子问题,无后效性,有重复子问题。
解题思路
状态转移表法
一般我们会采用二维数组来保存每一步决策的状态,如果状态较多可以采用三维四维数组,因为状态太多所以不太适合用这个方法。
方法如下:(代码和题目在下面,纪念下根据方法手撕出来的呦~)
- 先用回溯方法实现算法
- 画出递归树,找到重复子问题
- 画一个状态表,往往是一个二维数组,这个二维数组分为行、列、数值。
我们根据题目要求,模拟递推我们的决策过程,来填写状态表表,这个递推的过程翻译成代码就是动态规划的过程,即状态转移表法。
思考过程:
1 | public class MinDist { |
状态转移方程
完成状态转移方程,然后将状态转移方程翻译成代码。例如上面例子,状态转移方程是
1 | min_dist(i, j) = w[i][j] + min(min_dist(i, j-1), min_dist(i-1, j)) |
示例
0-1背包问题
1 | /** |
最短路径问题
如图:
1 | package dp |
找零问题
1 | //纸币找零动态规划求解 |
查找莱温斯坦距离和最大共有子串长度
todo
查找数组递增子序列
动态转移公式:
如果:array[i] < array[j]==>state[i][j] = state[i - 1][j - 1] + 1;
state[i][j] =Math.max(state[i][j - 1],state[i-1][j - 1]);
1 | public int getAscDP(int[] array) { |