专题 动态规划(Dynamic Programming,DP)的概述
概念解释
起源
动态规划(Dynamic Programming,DP),作为一种核心算法思想,其发明源于20世纪50年代美国数学家理查德·贝尔曼(Richard Bellman)在多阶段决策优化问题中的突破性研究。
概念
据笔者个人理解,DP是一种结合算筹学、博弈论、数学、概率论与统计学为一体的思想方法,依靠递推来实现,完成在一个复杂决策环境中决策出合法解的技术或思想。
性质
1.重叠子问题
子问题是一个大问题的小的版本,计算流程是完全一样的。可以参考递归的意义;其次,子问题经过若干次计算,最终可以得到原问题。至于为何DP的效率很高?通过迭代枚举所有可能的情况(事实上叫做阶段,后面会提到),计算所有可能的子问题,保证不重不漏。而由于子问题的个数通常是多项式级别的,所以DP时间复杂度通常也是多项式级别的。
2.最优子结构性质。
在计算机科学中,与DP思想很接近的还有几类技术,分别是递归、递推以及分治。但是这几个技术又有不同,具体体现在——递归与递推是一种实现方式,递归是自调用函数的别称,而递推底层逻辑则是迭代。至于分治,体现的则是一种“分而治之”的思想,常常用递归实现。
以上这些内容只是为了便于区分DP,而DP也具有上述“分而治之”的思想,但是在这里却是另一个定义,名为“最优子结构”。事实上,最优子结构保证了DP出来的结果就是最优的,并且无需证明(但是实际上是否是最优子结构是需要证明的)。这是其意义之一。
可以简单地理解什么是最优子结构,那就是将原问题划分,保证具有这么一种结构:大问题包含小问题的解,并且小问题可以推导出大问题的解。所以每次“决策”(后面也会提到)都是所要求的解的形式或者说是方向,经过若干次计算最终得到的就是所求解。这个保证实际上就是前两条保证的。
3.无后效性
我们引入有向无环图(DAG)来辅助理解DP的无后效性。DAG是一种可以体现“依赖关系”的图,在DP中,依赖关系体现为“一个阶段是由另外几个阶段转移而来的”。请注意,在这里不允许有着DAG一样的依赖。一旦发生依赖,意味着有两个局面互相到达,这就违反了前面两条性质,这个问题就不可以用DP解决。为了解释清楚,请参阅第四点的“阶段和状态”有关内容。(以下默认已经知晓阶段和状态的内容)现在我们定义并总结:无后效性是指——状态之间不能互相到达,阶段不可逆的性质。
DAG是存在严格的依赖顺序的。更准确地说,是拓扑排序存在严格的依赖顺序的。所以DP的逻辑常常基于DAG的逻辑,以保证对上面的三条性质的诠释。
笔者在这里又将前三条性质统称为“DP最优性的保证”,
即"
"。
4.阶段,状态与转移
现在讨论的两个术语概念属于DP的结构。
阶段:
程序是按照一定的顺序执行的;逻辑也是具有顺序的。像这样,由于子问题之间具有某种处理顺序而体现出来的关系,称为“阶段”。阶段是DP运行的底层逻辑之一。按照阶段去执行程序可以得到所求解,证明是平凡的。事实上,前面的三条性质已经保证了“最优”,得到最优的结果,只需要按照阶段来完成。
状态:
可以理解为一个局面。用若干条信息,描述一种特有的局面,这种特有的局面,像这样的局面,称作状态。状态是计算结果时的中间过程,状态之间会发生转移,也就是彼此是有关联的(从前,有依赖关系)。而这种依赖关系又是具有一定的顺序的,结合前面所讲,也就是依照阶段。
转移:
状态之间彼此相互关联,并且可以通过变量的改变影响状态发生改变,同时这种转变是有顺序的,即是依照阶段的。这种逻辑性的执行称为:转移。DP的转移通常是使用状态转移方程。
理解应用
应用
前面大致讲清楚了DP的概念,略有生涩。请结合实际应用完成理解。
工具性的思想
DP事实上是一种思想。在一个复杂的决策环境和逻辑链环中,按照自己所需要的,运用DP决策出所求结果。
常见在算法竞赛中的应用
请注意:在实际应用中常常会遇到多重for循环枚举的情况,有的时候是不需要思考为什么什么循环应该放在外面,什么循环应该放在里面的。这个细节市面上基本上没有算法资料或者论文讲到过。
这里的细节事实上很耐人寻味,比如笔者引用笔者的一篇文章,也即专题 线性DP-背包问题之完全背包。这一篇文章笔者当时并没有很严谨的按照一种科学的思维定量地分析其中的逻辑规律,而只是定性地分析了,浅尝辄止罢了。所以在本文中,笔者愿意按照一种科学的思维去解释这个比较复杂的逻辑。
那两个题笔者当时总结的是
- 组合数问题(顺序无关):固定面额顺序,逐步累加方案数。
- 排列数问题(顺序敏感):对每个金额
i,遍历所有可能的面额a[j],允许不同顺序的组合(如1+2和2+1算两种方案)。
这是表面的、易于觉察的细节,不是我们真正可以总结出来成为经验的细节。下面我们再回味一下这两句话和那两道题,从最原始DP的思考模式去入手。
我们前面讲了很多关于DP性质的内容,先给出那两道题的状态定义。
定义:dp[i]表示凑出金额i时的(方案数或最小纸币数),通过遍历纸币面额更新状态。
现在是重点。前面讨论过
由于子问题之间具有某种处理顺序而体现出来的关系,称为“阶段”。阶段是DP运行的底层逻辑之一。按照阶段去执行程序可以得到所求解
而这种依赖关系又是具有一定的顺序的,结合前面所讲,也就是依照阶段。
同时这种转变是有顺序的,即是依照阶段的。
这三段话都体现了重点是阶段。也就是说,在实际的程序设计时,最外层的循环的底层逻辑其实就是所谓的阶段,而用一个或几个变量的变化去描述阶段的变化,这就牵扯出了不同的状态。归根结底, 阶段是决定程序走向的,应该放在最外面;决策是基于状态和状态的转移,放在里面。
注:本文极少部分内容有节选其它参考书和文章。

浙公网安备 33010602011771号