背包九讲学习笔记 第三讲 多重背包

题目

给n种物品和一个容量为v的背包,每种物品最多有num[i]件可用,每个物品都有一个体积volume[i]和价值value[i],求背包最多能装多少价值的物品?

基本算法

dp[j]表示容量为j的背包最多能装多少价值的物品.
dp[j]=max{dp[j-k*volume[i]]+k*value[i]} 0<=k<=num[i]
复杂度O(VNnum[i])

如果转换为01背包,相当于物品数为∑num[i]的01背包问题,复杂度O(V∑num[i])

二进制优化

将Mi件体积为Ci,价值为Wi的物品,可以转换为ceil(logMi)件物品:
每件物品的体积和价值都乘以一个系数,系数依次为1,2,4,8,…..,2^(k-1),Mi-(2^k-1).
其中k满足是Mi-(2^k-1)>0的最大整数

正确性:
注意到做01背包时会给出每件物品取或不取的最优情况,也即如果直接分成Mi件物品,跑完之后总会给出最优取几件的情况.
而由分成的这些物品和0(不取),可以组合成0到Mi之间的任何一个数字.按01背包跑完后,同样会给出一摸一样的最优取几件的情况.
两者是完全等效的.

复杂度
O(V∑log(Mi))

实现
对于物品num[i],volume[i],value[i]
如果num[i]*volume[i]>=V 直接按照完全背包来做这个物品即可
令k=1;
当k<num[i],时
—对物品k*volume[i],k*value[i]做01背包
—num[i]-=k;
—k<<=1
最后,对num[i]*volue[i],num[i]*value[i]做01背包

O(VN)的解法

对于基础的状态转移,使用单调队列来优化,达到O(VN)的复杂性
在优化dp的时候再去学习

例题

51nod 1086 背包问题V2 多重背包裸题
完全如上文所说,有几个需要注意的点:
1.要非常熟练完全背包和01背包的基础代码写法.
2.关于k的循环选取应该有更好的方式

    int n=read(),v=read();
    for(int i=1;i<=n;i++)
    {
        volume[i]=read();
        value[i]=read();
        num[i]=read();
    }
    for(int i=1;i<=n;i++)
    {
        if(volume[i]*num[i]>=v)
        {
            for(int j=volume[i];j<=v;j++)
                dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
            continue;
        }
        for(int k=1;k<num[i];k<<=1)
        {
            int volu=k*volume[i],valu=k*value[i];
            for(int j=v;j>=volu;j--)
                dp[j]=max(dp[j],dp[j-volu]+valu);
            num[i]-=k;
        }
        int volu=num[i]*volume[i],valu=num[i]*value[i];
        for(int j=v;j>=volu;j--)
            dp[j]=max(dp[j],dp[j-volu]+valu);
    }
    printf("%d\n",dp[v] );

以上是略微改过的代码,用了一个完全背包两个01背包.
有一个疑惑点:如果直接将volu和value左移位,会谜之wa,保存这个问题,先略.

习题集

见后文.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值