有了这个,如果只是单纯地在枚举时的i从w(x-1)到x-1,这必然作用不大,不能起到实际效果。动态规划常见的另一个角度,“当前状态能够改进哪些”,还是看看文章吧(见《1D/1D动态规划优化初步》):
这样,每一步过程中某些状态的决策可能不是最优的,但是当算法结束的时候所有状态对应的决策一定是最优的。一开始,只有f(1)的函数值被计算出来,于是所有状态的当前最优决策都是1。
111111111111111111111111111111111111111111111111111111111111111
现在,显然f(2)的值已经确定了:它的最有决策只能是1。我们用决策2来更新这个决策表。由于决策单调性,我们知道新的决策表只能有这样的形式:
111111111111111111111111111111222222222222222222222222222222
这意味着我们可以使用二分法来查找“转折点”,因为如果在一个点x上,如果决策2更好,则所有比x大的状态都是决策2更好;如果x上决策1更好,则所有比x小的状态都是决策1更好。
现在决策1和决策2都已经更新完毕,则f(3)业已确定,现在用决策3来更新所有状态。根据决策单调性,现在的决策表只能有以下2种类型:
11111111111111111111111111111111122222222222222222233333333333
1111111111111111111111111333333333333333333333333333333333333
而这样的决策表示绝对不会出现的:
111111111111333333333333333333322222222222222222222222222222,不可能。
那么,我们的更新算法就是:考察决策2的区间[b,e]的b点上是否决策3更优,如果是,则全部抛弃决策2,将此区间划归决策3;如果否,则在决策2的区间[b,e]中二分查找转折点。如果第1问的回答是“是”,则用同样的方法考察决策1。
推演到这一步,相信决策单调性的实现算法已经明了了:使用一个栈来维护数据,占中的每一个元素保存一个决策的起始位置与终了位置,显然这些位置相互连接且依次递增。当插入一个新的决策时,从后到前扫描栈。
我觉得这个决策单调性还是需要一点直觉和简单的证明的。用反证法,假设出现w(i)> w(j)(i < j),可以发现若f(j)取w(i)比w(j)会更优。
而对于“玩具装箱”,转移方程如下:
c是某个玩具的长度,我这下标从0开始。
很多部分上面已经讲了,但有些细节:
对于w数组的维护,是不可能直接维护的。从栈里二分扫描也是比较麻烦的,可以在w数组上打懒标记即可。如:
11111111111111111111111111111111122222222222222222233333333333,
就可以打这样的标记(加粗数字),下面的是w数组保存的值:
10000000000000000000000000000000020000000000000000030000000000,“0”代表空,当扫描时遇到0,则重复前一个的即可。
这题还有个不同的地方:经典模型是从0到x-1,而这里是0到x,所以还要特殊判断一下。