【51nod 1086】 背包问题 V2 (多重背包)

多重背包裸题…

c c 表示物品个数,w 表示物品体积, v v 表示物品价格,以下不做特殊解释

多重背包的问题,我们可以物品拆开,然后用01背包来求,复杂度是 O(mc)

但显然这样复杂度比较大,有两种优化:二进制分组、单调队列优化

先来说下二进制分组,也是把物品拆开,只不过不是拆成一个,而是根据二进制拆,因为我们知道1、2、4、8…可以组成所有数。因此把一个物品拆成log份,复杂度为 O(mlogc) O ( m ∑ l o g c )

而单调队列优化可以优化到 O(nm) O ( n m )
考虑转移方程 f[i][j]=max(f[i1][jkw[i]]+kv[i]) f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) 其中 0<=k<=min(c[i],jw[i]) 0 <= k <= m i n ( c [ i ] , j w [ i ] )
a=j/w[i],b=j a = j / w [ i ] , b = j ,即 j=aw[i]+b j = a ∗ w [ i ] + b
转移方程变为 f[i][wv[i]+b]=max(f[i1][(ak)w[i]+b]+kv[i]) f [ i ] [ w ∗ v [ i ] + b ] = m a x ( f [ i − 1 ] [ ( a − k ) ∗ w [ i ] + b ] + k ∗ v [ i ] ) 其中 0<=k<=min(a,c[i]) 0 <= k <= m i n ( a , c [ i ] )
再令 s=ak s = a − k ,得到 f[i][aw[i]+b]=max(f[i1][sw[i]+b]sv[i])+av[i] f [ i ] [ a ∗ w [ i ] + b ] = m a x ( f [ i − 1 ] [ s ∗ w [ i ] + b ] − s ∗ v [ i ] ) + a ∗ v [ i ] 其中 amin(a,c[i])<=s<=a a − m i n ( a , c [ i ] ) <= s <= a
因此枚举 b b ,用单调队列维护 f[i1][sw[i]+b]sv[i] 最大值即可

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10010
#define mp make_pair
pair<int,int>q[N];
int n,m,w[N],v[N],c[N],f[N*5];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
    for(int i=1;i<=n;i++){
        c[i]=min(c[i],m/w[i]);
        for(int d=0;d<w[i];d++){    //枚举余数 
            int st=1,ed=0;
            for(int j=0;j<=(m-d)/w[i];j++){
                int tmp=f[j*w[i]+d]-v[i]*j;
                while(st<=ed && tmp>=q[ed].first) ed--;
                while(st<=ed && q[st].second<j-min(c[i],j)) st++;
                q[++ed]=mp(tmp,j);
                f[j*w[i]+d]=q[st].first+j*v[i];
            }
        }
    }
    printf("%d\n",f[m]);
    return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值