背包问题详解

秋招对我好点!!!

参考链接

什么是背包问题?

背包分类:
在这里插入图片描述

对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。

而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。

01背包问题

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

分析:

  1. 背包的容量限定(也可以不是背包 固定大小的容器都可)
  2. 背包中的物体有两个互相限制的变量,价值和重量 ,背包问题即保证特定重量的前提下,计算出最大价值
  3. 最重要一点,物体只能用一次!!!

【暴力解法】
物品取或者不取,两种情况,用回溯法去计算,时间复杂度o(2^N)

【动态规划解法】

二维dp数组01背包

动规五部曲!

  1. 确定dp数组以及下标的含义

(因为有两个变量,物体数 和 背包容量 ,价值是dp数组表达的结果)

dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

  1. 确定递推公式

两种情况

  • 第i个物品不放 此时的价值跟第i-1个物品的情况一样,dp[i][j] = dp[i-1][j]
  • 第i个物品放 记住此时的物品数是i,背包容量是j ,因此此时的价值只需要把第i-1个物品的背包价值加上第i个物品的价值就可以了,dp[i][j] = dp[i-1][j-weight[i]] + value[i]

两种情况取最大值就可以了
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

  1. dp数组如何初始化

从dp[i][j]出发
如果背包容量j为0的话,那么价值肯定是0,即dp[i][0] = 0

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

如果放入第i=0物品,又要分两种情况,如果weight[0]的重量比背包还重,那dp[0][j]为0,否则dp[0][j]就是value[0]的价值

所以要记住!初始化需要看递推公式需要什么!!!!

  1. 确定遍历顺序

比较重要的点!

在如下图中,可以看出,有两个遍历的维度:物品与背包重量
遍历的目的是把二维dp数组填满,所以还是关注递推公式!

两种遍历顺序

  • 先物品 后重量
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
    for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];  // 如果价值大于背包价值  就没必要放了
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
    }
}
  • 先重量 后物品
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
    }
}
  1. 举例推导
    检验一下!

一维dp数组01背包

动规五部曲!

  1. 确定dp数组以及下标的含义

在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

  1. 一维dp数组的递推公式

还是二维数组一样,加不加当前i物品

加or不加,这是一个question

因此,递推公式为:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

  1. 一维dp数组如何初始化

关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。
根据递推公式,只需要保证dp[0] = 0 即可

  1. 一维dp数组遍历顺序

重点!!

只能先遍历物品,后遍历背包,并且背包的顺序是倒序!

  • 【先遍历物品,后遍历背包】为了保证不会dp[j]一次放入一个物品
  • 【倒序】是为了保证0的物品不会重复放入
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}
  1. 举例推导dp数组

举例检验!


完全背包问题

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

分析:

  1. 背包的容量限定(也可以不是背包 固定大小的容器都可)
  2. 背包中的物体有两个互相限制的变量,价值和重量 ,背包问题即保证特定重量的前提下,计算出最大价值
  3. 最重要一点,物体能用无数次!!!

完全背包跟01背包最大的不同就是一个物品可以被拿无数次!

在01背包的解析中,我们讲解了遍历背包时倒序的目的是为了让每个物品只被添加一次,因此完全背包要从小到大去遍历!

01背包的核心代码:

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

完全背包的核心代码:

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

时刻记住,扣住迭代公式!
dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值