计算机归根结底只会做一件事:穷举。
所有的算法都是在让计算机【如何聪明地穷举】而已,动态规划也是如此。
动态规划是自底向上,递归树是自顶向下
为什么动态规划一般都脱离了递归,而是由循环迭代完成计算。
【DP的核心思想】
DP为什么会快?
无论是DP还是暴力,我们的算法都是在可能解空间内,寻找最优解。来看钞票问题。暴力做法是枚举所有的可能解,这是最大的可能解空间。DP是枚举有希望成为答案的解。这个空间比暴力的小得多。
也就是说:DP自带剪枝。DP舍弃了一大堆不可能成为最优解的答案。譬如:15 = 5+5+5 被考虑了。15 = 5+5+1+1+1+1+1 从来没有考虑过,因为这不可能成为最优解。
从而我们可以得到DP的核心思想:尽量缩小可能解空间。
在暴力算法中,可能解空间往往是指数级的大小;如果我们采用DP,那么有可能把解空间的大小降到多项式级。一般来说,解空间越小,寻找解就越快。这样就完成了优化。
【DP的操作过程】
一言以蔽之:大事化小,小事化了。
将一个大问题转化成几个小问题;求解小问题;推出大问题的解。
【如何设计DP算法】
下面介绍比较通用的设计DP算法的步骤。
首先,把我们面对的局面表示为x。这一步称为设计状态。对于状态x,记我们要求出的答案(e.g. 最小费用)为f(x).我们的目标是求出f(T).找出f(x)与哪些局面有关(记为p),写出一个式子(称为状态转移方程),通过f(p)来推出f(x).
【DP三连】
设计DP算法,往往可以遵循DP三连:
我是谁? ——设计状态,表示局面
我从哪里来? 我要到哪里去? ——设计转移
设计状态是DP的基础。接下来的设计转移,有两种方式:
- 一种是考虑我从哪里来(本文之前提到的两个例子,都是在考虑“我从哪里来”);
- 另一种是考虑我到哪里去,这常见于求出f(x)之后,更新能从x走到的一些解。这种DP也是不少的,我们以后会遇到。
总而言之,“我从哪里来”和“我要到哪里去”只需要考虑清楚其中一个,就能设计出状态转移方程,从而写代码求解问题。前者又称pull型的转移,后者又称push型的转移。
区间动态规划
区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。
令状态 f(i,j) 表示将下标位置 i 到 j 的所有元素合并能获得的价值的最大值,那么 f(i,j)=max{f(i,k)+f(k+1,j)+cost},cost 为将这两组元素合并起来的代价。
以最后一个元素为终点的动态规划,若元素状态最大值可确定,可增加尾结点最大值,类似链表的默认头节点,方便处理。
分块动态规划,以最后一个元素为最后一块区间,和前面节点合并区间,动态规划。
参考链接
什么是动态规划(Dynamic Programming)?动态规划的意义是什么?
五大基本算法之动态规划算法
买卖股票的最佳时机 II
合并石头的最低成本