算法解析之感想---单调队列优化多重背包思路

多重背包问题朴素时间复杂度为O(NMS)(这里S是所有物品的数量s之和),经过二进制优化后时间复杂度为O(NMlog2S),这个复杂度已经能够应付大多数题了,但对于某些特别卡时间的题(比如N*M=10的7次方),仍然会TLE。这时,可以用单调队列优化,时间复杂度降为O(NM)。 其优化思路是将当前重量限制取余物品重量数得到余数相同的归为一类,因此可以分为[0,V[i]-1]供V[i]类,其中对于每一类可以用单调队列优化,此时均摊分析时复杂度为O(M)。


首先看一下多重背包问题的朴素转移方程:
F[i][j] = max{F[i-1][j-x*w[i]]+x*v[i]} (0<=x<=s[i], j>=x*w[i])
如果使用滚动数组,忽略i这一维,设w0=w[i](重量),v0=v[i](价值),s0=s[i](数量),得:F[j] = max{F[j-x*w0]+x*v0} (0<=x<=s0, j>=x*w0)
看上去这和单调队列木有神马关系,因为决策下标(j-x*w0)不是一个整数区间,中间是有间隔的。然而可以发现,这个方程的限制条件“0<=x<=s0,j>=x*w0”,也就是x的下界是max{0, j/w0(下取整)}(即只有当x取这里面的数的时候可以实现在最多有x个物品的限制下状态正确转移),当j单调递增时,这个下界也是单调递增的。这满足单调队列优化的条件中的“决策下标的下界单调”……不是,还不能这样说,因为这里的决策下标是j-x*w0,而不是x。那么怎样才可以把决策下标变为x?


将决策下标(题目中的资源限制)按照模w0的余数进行分类,可以分成w0类,分别对应模w0余0、余1……余(w0-1)的情况。 这时,上面方程中的所有决策下标j-x*w0都是同一类的,同类的可以使用单调队列优化。进一步,设q =j/w0(下取整),r=j%w0,则j=q*w0+r,对于某个决策下标j',设k=(j'-r)/w0,即j'=k*w0+r。显然可以发现,k的取值范围是:k>=0且q-s0<=k<=q (因为,首先要满足每类商品有s0个的限制,因此只有k去在这个范围内的可以是q在商品有s0的个数限制下进行正确的状态转移),也即k的下界是max{0,q-s0}——随j的单调而单调。
然后,转移方程可以改为(这里把r当成一个已知量了):F[q*w0+r] = max{F[k*w0+r]+(q-k)*v0}(q-k:买了q-k个) (k>=0且q-s0<=k<=q)
即F[q*w0+r]=max{F[k*w0+r]-k*v0}+q*v0 (k>=0且q-s0<=k<=q)
设G[k]=F[k*w0+r]得:
G[q]=max{G[k]-k*v0}+q*v0 (k>=0且q-s0<=k<=q)

这个方程已经可以使用单调队列来优化了!


参考文献:

1.《背包九讲》

2.http://www.cppblog.com/MatoNo1/archive/2011/07/05/150231.html(主题思路)

3.http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html(国家集训队2009论文集浅谈几类背包题)

你可能感兴趣的:(C++)