HDOJ刷了一些动态规划(DP)问题,做些总结.
1 序列
目前看来,所有的动态规划问题基本上都存在一个序列,序列中的元素可以按照某种顺序唯一地排序.更抽象地说,排序的方式通常是这些元素在某个事件发生过程的时间轴上的位置.
我们说”序列”而非”集合”,是因为元素的唯一排序方式是非常重要的.因为这样我们就可以将精力集中于组合而非排列.在我看来,动态规划问题在本质上是一种组合问题,这样的问题总是可以通过枚举序列元素的全部组合而得以解决.
问题中的序列有时很明显,有时不明显(2059),有时是二维的(2571),甚至干脆没有显示给出(2084).不管怎样,我认为解决动态规划问题的首要任务就是明确这个序列.序列明确以后,我们就可以直接”腰斩”这个问题并考虑其”下半身”,即直接从序列的第i个元素开始思考问题.
2 问题的表示
由于动态规划问题总是关联着一个序列,因此我们可以用序列表示这个问题.比如用[0:]表示原问题,用[i:]表示从问题的”下半身”.就像在物理中我们总是将物体抽象成一个质点一样,将动态规划问题抽象成一个序列对我们进一步思考问题有很大的帮助.
当然,没必要煞有介事地用上[i:]这个类似于切片的语法,直接用数字0表示原问题也未尝不可–这里的关键是内容,而非形式.我没有正式地对符号进行定义,因为接下来的讨论大多是描述性的,严格定义符号显得过分隆重.
另一方面,用[0:]这样的形式表述问题是不全面的,因为很多问题的完整描述需要额外的变量.比如对于完全背包问题,当序列是[0:]时,我们需要额外的一个变量w来表示背包的剩余容量.这样对问题的完整表述应该是([0:],w),或者随意一些,(0,w).w是必要的,(0,w)和(0,x)表示的是同一问题在两个不同状态下的解,或者干脆就是两个不同问题.
这样,对于一个问题,我们既可以将其表示成[i:]也可以表示成(i,j).尽管符号的使用非常随意,但是引入一些符号确实能帮助我们更方便地分析问题.下面是我在分析问题时使用的一些符号.
符号 | 意义 |
---|---|
a[i] | 序列的第i个元素 |
[i:] | 从i号元素开始的序列,或者与这个序列相关的问题 |
(i) | 思考问题[i:]时需要枚举的集合,或者第i个决策阶段,详见后面 |
(i,j), (i,j,k,…) | 问题[i:]的另一种表述方式,此时我们强调表述问题的额外变量j,k等 |
ti,j[x] | 问题(i,j)在决策x下的局部最优解,详见后面 |
s[i,j] | 问题(i,j)的解 |
无论如何,就像我刚才说的,这里重要的是内容而非形式.