完全背包问题


先来一个奇妙的比喻,完全背包之于0/1背包,就仿佛是apple A15之于A14。

格式

n个重量和价值分别为wivi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。在这里,每种物品可以挑选任意多件。

回顾

我们之前在解决0/1背包的时候,采用的是记忆化搜索的方式,把当前的情况存放在一个二维数组中,每当便利到这个新情况的时候,就拿当前新情况的值和之前的最优解进行比较,谁的价值更大就保留谁。

题解

我们可以在0/1背包的基础上进行修改,首先0/1背包的
i表示物品,j表示容量,我们只有两个选择,拿或者不拿
W[i][j]=max(W[i-1][j]/*这是不拿的情况*/,W[i][j-wi]+vi/*拿的情况*/)
现在我们相当于有了无限个物品i,所以我们可以这么来,我们可以把两个物体看成一个新物体,体积翻倍,价值翻倍,三个,四个同理,只要在会看的过程中不把背包的容量变成负数,我们就可以拿n个物体再和之前的比较,写成代码的形式就是

int k=1;//k表示件数,从1件开始,由于我们不需要知道物品i拿了多少件,并不需要保存k
while(j>=k*w[i]){
    W[i][j]=max(W[i-1][j],W[i][j-k*w[i]]);   
}

这就是我们的转移方程了。
最后输出的方式和0/1背包一样。

插一嘴,这里并不一定要一个二维数组来存贮

我们可以用一维数组来记录结果,这个方法叫做滚动数组
因为每次拿东西的顺序是都是下一个,所以我们二维数组第一维(表示第几个物品)可以省略掉。
我们每次都从背包的最大容积处下手,然后倒着向左走。
为什么要倒着呢,因为正着向右走的话会把上一个物品的数据给覆盖掉,也就是说我们就丢失了不拿这个物品的记录了这就完蛋了~
使用的有点很显然,把空间复杂度为O(nv)的优化O(v),妙哉啊~

AC代码(滚动数组版)

#include<iostream>
using namespace std;

const int MAX_SIZE=110;
const int MAX_RONGJI=10010;
int n,W,w[MAX_SIZE],v[MAX_SIZE],W_Bag[MAX_RONGJI];//物品数量,背包容积,物品体积,物品价值,后期存储数据用的一维数组

int main(){
    cin >> n >> W;
    for(int i=0;i<n;i++){//读入物品的容积和价值
        cin >> w[i] >> v[i];
    }
    for(int i=0;i<n;i++){//标记物品
        for(int j=W;j>0;j--){//标记背包容量
            int k=1;
            while(j>=(k*w[i])){//背包还装的下就行
                W_Bag[j]=max(W_Bag[j],W_Bag[j-k*w[i]]+k*v[i]);
                k++;
            }
        }
    }
    cout << W_Bag[W];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值