多重背包
F[i][j]表示对容量为j的背包,处理完前i种物品后,背包内物品可达到的最大总价值。
num[i]表示第i种物品的数量,cost[i]表示第i种物品的花费,value[i]表示第i种物品的价值。
记mi[i] = min(num[i], j / cost[i])。
放入背包的第i种物品的数目可以是:0、1、2……,可得转移方程:
F[i][j] = max { F[i - 1] [j – k * cost[i] ] + k * value[i] } (0 <= k <= mi[i])
上面的式子就是朴素的多重背包转移方程式。
二进制优化
二进制优化是基于对于每一种物品的数量分成件数为1,2,4,8……的多个物品
一种物品可以分为logn件不同的物品
然后就等同于01背包的问题
复杂度:O(n*m*logn)
单调队列优化
假设 a = j / cost[i],b = j % cost[i],即 j = a * cost[i] + b
代入朴素的转移方程式,并用k替换a - k得:
F[i][j] = max { F[i - 1] [b + k * cost[i]] - k * value[i] } + a * value[i]
(a – mi[i] <= k <= a) 很重要的判断,用于去除队首的无效值
单调队列维护队首的max F[i - 1] [b + k * cost[i]] - k * value[i]
从对位添加F[i - 1] [b + k * cost[i]] - k * value[i]时,若大于队尾,则删除队尾
复杂度:O(n*m)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
for(int i=1;i<=n;i++) { for(int j=0;j<cost[i];j++) { q[head=tail=1]=make_pair(f[j],0); for(int k=j+cost[i];k<=m;k+=cost[i]) { int a=k/cost[i],t=f[k]-a*value[i]; while(head<=tail&&q[tail].first<=t)tail--; q[++tail]=make_pair(t,a); while(head<=tail&&q[head].second+num[i]<a)head++; f[k]=max(f[k],q[head].first+a*value[i]); } } }