多重背包
6. 多重背包问题 III - AcWing题库
考虑公式如上图所示。发现 \(f[i,j]\) 与 \(f[i,j-v]\) 相比,少了最后面一项,这就好比我们知道一个集合的最大值,又知道其中一个数的最大值,我们是不能确定剩余部分的最大值。
如图
颜色奇怪,还请见谅。
就是说我们知道外面蓝色圆形内的值,又知道中间的空心部分,无法确定红色部分的值。
那应该怎么办?
我们思考背包的本质:
- 01背包,只有一种转移方式。
- 完全背包,也有多种转移方式,但是刚好满足 \(f[i,j]\) 比 \(f[i,j-v]\) 多 \(f[i,j-v]\) 一项,可以正着循环直接转移。(\(f[i,j]=f[i,j-v]+w\))。
- 完全背包,要求一个长度为 \(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;
}