多重背包

6. 多重背包问题 III - AcWing题库

image-20230827203618431

考虑公式如上图所示。发现 \(f[i,j]\)\(f[i,j-v]\) 相比,少了最后面一项,这就好比我们知道一个集合的最大值,又知道其中一个数的最大值,我们是不能确定剩余部分的最大值。

如图

image-20230827203849452

颜色奇怪,还请见谅。

就是说我们知道外面蓝色圆形内的值,又知道中间的空心部分,无法确定红色部分的值。

那应该怎么办?

我们思考背包的本质:

  1. 01背包,只有一种转移方式。
  2. 完全背包,也有多种转移方式,但是刚好满足 \(f[i,j]\)\(f[i,j-v]\)\(f[i,j-v]\) 一项,可以正着循环直接转移。(\(f[i,j]=f[i,j-v]+w\))。
  3. 完全背包,要求一个长度为 \(s\) 的区间(开头的数可能不满足,但是我们可以很方便的处理这个问题)。

发现3就可以用滑动窗口求最值。

而对于 \(+w\) 的倍数不一样,可以在入队时减去当前的倍数,前面的不变即可,具体看代码。

#include<cstdio>
#include<algorithm>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while

const int N=1010,V=20010;
int n,v,f[2][V],a[N],b[N],c[N],q[V],qid[V];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif
    scanf("%d%d",&n,&v);
    E(i, n)
        scanf("%d%d%d",a+i,b+i,c+i);
    E(i, n)
        L(r, a[i]){
            int ls=(i-1)&1,no=i&1,hh=0,tt=-1;
            for(int k=r,t=0;k<=v;k+=a[i],++t){
                if(hh<=tt&&qid[hh]<k-c[i]*a[i])++hh;
                f[no][k]=max(hh<=tt?q[hh]+t*b[i]:0,f[ls][k]);
                // printf("t=%d,f[%d][%d]=%d\n",t,i,k,f[i&1][k]);
                while(hh<=tt&&q[tt]<=f[ls][k]-t*b[i])--tt;
                q[++tt]=f[ls][k]-t*b[i];
                qid[tt]=k;
            }
        }
    printf("%d",f[n&1][v]);
    return 0;
}
posted @ 2023-08-27 20:42  wscqwq  阅读(2)  评论(0编辑  收藏  举报