动态规划(三):01背包问题

这篇博客详细介绍了01背包问题,包括背包问题概述、01背包的二维和一维解法,并通过例题解析了如何求解01背包的最大价值和最大组合数,涉及动态规划的递推公式和代码实现。
摘要由CSDN通过智能技术生成

一、背包问题概述

背包问题为有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。将这些物品装进背包,怎么装价值最高。 根据条件不同,可以分为以下几类:

  1. 01背包:每件物品只有一个,要不拿一个,要不不拿
  2. 完全背包:每件物品有无数个,可以拿0-任意个
  3. 多重背包:不同物品数量不同
  4. 分组背包:多种物品组成一个小组,每组只能拿一种
  5. 组合背包:01背包,完全背包与多重背包的组合体

leetcode上学会01背包,完全背包和多种背包足够了,因此这里只讲这三种。全面的背包问题讲解推荐《背包九讲》

二、01背包二维解法

1、问题概述

有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只有一个,求解将哪些物品装入背包里物品价值总和最大。

2、dp数组定义

定义dp数组为二维数组,dp[i][j]表示01背包条件下物品编码为0-i,背包重量为j时的最大价值总和
在这里插入图片描述

2、递推公式

dp[i][j]可以由两种情况推导而来:

  • 在增加物品i时,不将其放入背包:此时dp[i][j]dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j] = dp[i - 1][j]
  • 在增加物品i时,将其放入背包:此时dp[i][j]dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]]为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i][j] = dp[i - 1][j - weight[i]] + value[i]

综上,dp[i][j]取两种情况下价值最大的情况:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

3、dp数组初始化

由于dp[i][j]是由dp[i-1][j]推导出来的,因此要初始化全部的dp[0][j]

  1. 首先将dp数组全部初始化为0
  2. dp[i][0]全部初始化为0
  3. 然后初始化只放物品0时候背包的最大价值dp[0][j]
//当前背包容量j大于等于物品0的重量时候,初始化dp[0][j] = value[0]
//这里是倒序遍历dp[0][bagWeight]~dp[0][0]实现上述功能,倒序遍历实现了每样物品只拿一个,当计算dp[0][j]的时候其前面的dp[0][j - weight[0]]是0,没放东西的,这里是为了和更复杂的背包问题初始化保持一致
// 初始化 dp
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
for (int j = bagWeight; j >= weight[0]; j--) {
   
    dp[0][j] = dp[0][j - weight[0]] + value[0];
}

4、遍历顺序

根据递推公式dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),dp[i][j]依赖其上方元素和左上方元素,因此先遍历背包(先横向遍历)和先遍历物品(先纵向遍历)都是可以的

5、完整代码

void test_2_wei_bag_problem1() {
   
    vector<int> weight = {
   1, 3, 4};
    vector<int> value = {
   15, 20, 30};
    int bagWeight = 4;

    /-----dp数组初始化-----/
    vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
    for (int j = bagWeight; j >= weight[0]; j--) {
   
        dp[0][j] = dp[0][j - weight[0]] + value[0];
    }

    /----遍历,递推公式---/
    for(int i = 1; i < weight.size(); i++) {
    // 遍历物品
        for(int j = 0; j <= bagWeight; j++) {
    // 遍历背包容量
            if 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值