多重背包单调队列优化

Posted on 2017-09-08 08:23  Amphetamine  阅读(198)  评论(0编辑  收藏  举报
for (int i = 1; i <= n; ++i) {
    Ni = Num[i]; Vi = V[i]; Wi = W[i];
    for (int j = 0; j < Vi; ++j) {
        Head1 = Tail1 = 0;
        Head2 = Tail2 = 0;
        Cnt = 0;
        for (int k = j; k <= m; k += Vi) {
            if (Tail1 - Head1 == Ni + 1) {
                if (Q2[Head2 + 1] == Q1[Head1 + 1]) ++Head2;
                ++Head1;
            }
            t = f[k] - Cnt * Wi;
            Q1[++Tail1] = t;
            while (Head2 < Tail2 && Q2[Tail2] < t) --Tail2;
            Q2[++Tail2] = t;
            f[k] = Q2[Head2 + 1] + Cnt * Wi; 
            ++Cnt;
        }
    }
}
View Code

 

考虑多重背包与01背包,完全背包的区别。

多重背包每种物品只能选最多m个。

那么我们的任务就是,如何控制多重背包最多选m个物品。

自然想到滑动窗口。当窗口宽度大于v[i]*m的时候把前面的元素删除

但是如果单纯的滑动,对与某一个f[k],f[k-v[i]]可能已经被这个物品更新过了

设V=a*vi+b;

设k=c*vi+d;

枚举c,d就可以均匀的枚举到所有小于V的值,且不重复,复杂度O(V)

于是枚举每一个d值,对于每个d再枚举c,当c>m时将队首删除

每一个枚举到的值用单调队列维护。每次f[k]就是队列最大值