01背包问题

1. 问题描述

请添加图片描述

【0-1背包——问题描述】有n件物品和一个最多能背重量为w的背包。第i件物品的重量时weight[i],得到的价值为value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
在这里插入图片描述
注:每个物品只有两种状态,取或者不取

2. 0-1背包——二维数组

【举例】背包的最大容量为4,物品的重量和价值如下

重量价值
物品 012
物品 123
物品 234

2.1 状态分析

       使用二维数组f[i][j]: 表示从下标为 [0-i] 的物品里任意放取,放进容量为 j 的背包,其价值总和最大为f[i][j]
在这里插入图片描述

2.2 转移方程

f[i][j]的值可以有两个方向推出

  • 不放入物品i:此时f[i][j] = f[i-1][j]。当物品i的重量大于背包j的重量时,物品i无法放入背包,所以背包的价值仍然和前面一个相同。当背包重量为1时,只能装入物品0,物品1和物品2无法装入,故背包价值仍等于前面一个的价值
    在这里插入图片描述

  • 放入物品i:此时f[i][j] = f[i-1][j-weight[i]] + value[i]当前背包的价值 = 没有加入物品i时背包的最大价值 + 物品i的价值f[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么f[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
    在这里插入图片描述

2.3 初始化

  • 如果背包容量为0,无论取哪些物品,背包所装物品的最大价值一定为0,故f[i][0] = 0

  • 根据转移方程 f[i][j] = max(f[i - 1][j], f[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。

    • f[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
    • 当背包容量小于物品0的重量时,背包所存放物品的最大价值为0;即当j < weight[0] 时,f[0][j] = 0;
    • 当背包容量大于等于物品0的重量时,背包可以存放此物品,故背包所存放物品的最大价值为物品0的价值,即当j >= weight[0]时,f[0][j] = value[0];
    	for(int j = 0;j < weight[0];++j){ //若数组f[i][j]预先初始化为0,此步可省略
    		f[0][j] = 0;
    	}
    	for(int j = weight[0];j <= bagweight;++j){
    		f[0][j] = value;
    	}
    

在这里插入图片描述

2.4 计算顺序

先遍历背包与先遍历物品一样

2.5 举例推导数组f[i][j]

在这里插入图片描述

2.5 C++实现

【算法实现】

int bag01(vector<int>& weight,vector<int>& value){
    int bagweight = 4; //背包最大容量
    vector<vector<int>> f(weight.size(),vector<int>(bagweight+1,0));

    //初始化
    for(int j = weight[0];j <= bagweight;++j){
        f[0][j] = value[0];
    }

    for(int i = 1;i < weight.size();++i){
        for(int j = 0;j <= bagweight;++j){
            if(j < weight[i])
                f[i][j] = f[i-1][j];
            else
                f[i][j] = max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);
        }
    }
    return f[weight.size()-1][bagweight];
}

【测试】

#include <iostream>
#include <vector>
using namespace std;


int main(){
    vector<int> weight = {1,2,3};//物品重量
    vector<int> value = {2,3,4};//物品价值

    cout << bag01(weight,value) << endl;

    return 0;
}

3. 0-1背包——一维数组

【思考】在使用二维数组进行计算时,转移方程:f[i][j] = max(f[i-1][j],f[i-1][j-weight[i]]+value[i]),假若把f[i-1]层拷贝到f[i]层,表达式可以表示为f[i][j] = max(f[i][j],f[i][j-weight[i]]+value[i]),则可以捡回为一维数组,只使用f[j].

3.1 确定状态

       使用一维数组f[j]:则表示为容量为j的背包所存放物品的最大价值为f[j]。

3.2 转移方程

f[i]的取值有两个选择,

  • 一个是未加上物品i时背包所存放物品的最大价值,即为前一个f[j]
  • 另一个是加上物品i时背包所存放物品的最大价值,即f[j-weight[i]] + value[i]

所以转移方程可以表示为:f[j] = max(f[j],f[j-weight[i]]+value[i]);

3.3 初始条件

       f[j]表示:容量为j的背包,所背的物品价值可以最大为f[j],那么f[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。

3.4 计算顺序

for(int i = 0; i < weight.size();++i){ //遍历物品
	for(int j = bagweight;j >= weight[i];j--){  //遍历背包容量
		f[j] = max(f[j],f[j-weight[i]]+value[i]);
	}
}

       背包容量倒序遍历是为了保证物品i只被放入一次。但如果正序遍历,那么物品0就会被重复加入多次。
       遍历物品的顺序和遍历背包容量的顺序不能弄翻,否则每个f[j]就只会放入一个物品。

3.5 C++实现

【算法实现】

int bag02(vector<int>& weight,vector<int>& value){
    int bagweight = 4;
    vector<int> f(bagweight+1,0);
    
    for(int i = 0;i < weight.size();++i){
        for(int j = bagweight;j >= weight[i];j--){
            f[j] = max(f[j],f[j-weight[i]]+value[i]);
        }
    }

    return f[bagweight];
}

【测试】

#include <iostream>
#include <vector>
using namespace std;

int main(){
    vector<int> weight = {1,2,3};
    vector<int> value = {2,3,4};
    
    cout << bag02(weight,value) << endl;
    return  0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值