理论—动态规划(Dynamic Programming)
- 求解过程是多阶段决策过程 每步处理一个子问题 可用于求解组合优化问题
- 使用条件:问题要满足优化原则或最优子结构性质,即:一个最优决策序列的任何子序列本身一定是相对子序列的初始和结束状态最优决策序列
递归实现与迭代实现
-
动态规划递归实现效率不高 原因在于同一子问题多次重复出现 每次出现都要重新计算
-
采用空间换时间策略 记录每个子问题首次计算结果 后续再使用时候直接取值(空间换时间)
-
迭代实现每个子问题只计算一次 从最小的子问题算起 考虑计算顺序 保证后面使用到的值前面已经做过计算
-
解的追踪 设计标记函数标记每一步的决策 考虑根据标记函数追踪解
比较
递归的时间复杂度高 空间较小
迭代的时间复杂度低 空间消耗多
要素:
- 划分子问题
- 想清楚是否满足优化原则 能否通过动态规划来解决
- 备忘录设计
- 标记函数
- 无后效性(很重要):即为了保证计算子问题能够按照顺序、不重复地执行 要求已经求解的子问题不受后续阶段的影响 即动态规划对状态空间的遍历就像有向无环图 结点对应状态 边即状态转移 转移的选取即动态规划的决策 所以我们在设置状态的时候要考虑到这一点
Leetcode题解以及DP模板
正确解题的前提是一定要明确好状态转移方程(即递推公式) 确定好dp数组的含义
然后就是初始化dp数组 和遍历顺序了 写完代码后一定要debug去看自己的dp数组推导变化是否和自己想的一样
53(找寻最大子数组和):
分析:将问题划分为 以某个元素为结尾的最大子数组和
dp[i]:以i下标元素为结尾的最大子数组和
那么状态转移方程就是
最后我们遍历一遍即可获取最大值
这里可以通过滚动变量的方式将代码改进优化 因为dp[i]的值之和dp[i-1]有关
动态规划和贪心算法的区别
如果一个问题有很多重复的子问题,那么用动态规划是最有效的
动态规划中的每一个状态一定是由上一个状态推导出来的,是有DP递推方程的,而贪心算法是没有推导的,关键是在于针对不同的问题去制定正确有效的贪心策略,然后每一步都直接从局部选取最优后,把每一步决策结果收集就得到了问题的解
可以用一个很简单的例子来区分他们
比如现在有一个背包和很多件物品,物品有重量和价值两个属性,每件物品只能选一次,如果不考虑重量即这个书包是承重是无限大的,让你选有限个数的物品来得到价值最大的背包,那你肯定是贪心算法每次去选当前最有价值的物品,这样最后你就得到了价值最大的背包,可是如果现在这个背包的承重是有限的了,那你就要动态规划怎么装物品来使得背包价值最大了。