P1776 宝物筛选

【题意】

多重背包问题,每种物品有限制个数

【分析】

做点分治的时候突然发现竟然不会单调队列优化多重背包,于是就跑来做这个绿题了

我们有二进制分组的$O(\sum_{i=1}^nlog(num[i])$

其实还有优秀的$O(nv)$做法

我们考虑转移方程$f[i][j]=max(f[i−1][j−w∗k]+v∗k) (k<=c)$

把它写成$f[i][j]=max(f[i−1][d+w∗k]−v∗k)+v∗s (s-k<=c)$这种形式,其中d为 j mod w[i] ,s为 j/w[i]

这样我们就显然可以对于各个余数做一下单调队列了

 

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e4+5;
int q[maxn],f[maxn],pos[maxn];
int head,tail,n,W;
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&W);
    int v,w,c,ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&v,&w,&c);
        if(!w) {ans+=c*v; continue;}
        int gs=W/w;
        c=min(c,gs);
        for(int d=0;d<w;d++)
        {
            head=tail=0;
            gs=(W-d)/w;
            for(int j=0;j<=gs;j++)
            {
                while(head<tail && f[d+j*w]-j*v>=q[tail-1]) tail--;
                pos[tail]=j;
                q[tail++]=f[d+j*w]-j*v;
                while(head<tail && pos[head]<j-c) head++;
                f[d+j*w]=max(f[d+j*w],q[head]+j*v);
            }
        }
    }
    printf("%d",ans+f[W]);
    return 0;
}

 

posted @ 2021-06-27 12:07  andyc_03  阅读(57)  评论(0编辑  收藏  举报