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; } } }
考虑多重背包与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]就是队列最大值
时间: 2024-11-05 10:53:42