【算法基础】多重背包与一维数组背包

前两天学了基础的背包的板子,但是发现不能满足一些题目的要求或者是空间复杂度过大

那有没有更加节约时间空间的写法呢?有的有的,我们先从多重背包问题引入吧

问题描述

有 NN 件物品和一个体积为 MM 的背包。第 ii 个物品的体积为 vivi​,价值为 wiwi​。每件物品只能使用 cici​ 次。

请问可以通过什么样的方式选择物品,使得物品总体积不超过 MM 的情况下总价值最大,输出这个最大价值即可。

输入格式

第一行输入两个正整数 N,MN,M。(1≤N,M≤100)(1≤N,M≤100)

接下来 NN 行,每行输入三个整数 vi,wi,civi​,wi​,ci​。(0≤vi,wi,ci≤100)(0≤vi​,wi​,ci​≤100)

输出格式

输出一个整数,表示符合题目要求的最大价值。

样例输入

4 5
1 2 3
2 4 1
3 4 3
4 5 2

样例输出

10

 这个问题的数据量比较小,我们可以用完全背包的写法来ac这题

代码如下

for(int i=1;i<=n;i++)
    for(int j=0;j<=m;j++)
    {
        dp[i][j]=dp[i-1][j];
        for(int l=1;l<=c[i];l++)
        if(j>=v[i]*l)
        dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]*l]+l*w[i]);
    }

但是当数据量变大到
1≤N,M≤2000
1≤vi​,wi​,ci​≤2000
时,我们会得到一个tle,甚至是mle

因为每件物品的数量可能会非常大,所以我们需要对数量进行拆分组合,有没有一种能组成任意数字的一种计算工具呢?有的有的,那就是二进制,我们对每个物品进行二进制拆分,使之被分为1,2,4,8,...的组合,这些组合能够组合成任意数字

得到以下读入拆分的代码

for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i]>>c[i];
        for(int x=1;c[i]>0;x*=2)
        {
            int times=(min(x,c[i]));
            weight.push_back(v[i]*times);
            value.push_back(w[i]*times);
            c[i]-=times;
        }    
    }

二进制拆分使每个物品消耗的时间从n变为logn

对于空间,我们可以使用一维数组进行dp的记录
不难观察到二维数组中状态转移方程中dp数组的变化只是第一个下标沿用上一层,也就是dp[i][j]基]本沿用dp[i-1][j],所以可以得出每一层只和上一层有关,于是我们可以简化dp数组为一层,并且为了不影响装不下第i个物品时的情况,所以我们应该逆序遍历,得到如下代码
 

vector<int>dp(m+1);
    for(int i=0;i<weight.size();i++)
    for(int j=m;j>=weight[i];j--)
    dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);

 于是我们就得到了多重dp的解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值