秋招对我好点!!!
什么是背包问题?
背包分类:
对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。
而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。
01背包问题
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
分析:
- 背包的容量限定(也可以不是背包 固定大小的容器都可)
- 背包中的物体有两个互相限制的变量,价值和重量 ,背包问题即保证特定重量的前提下,计算出最大价值
- 最重要一点,物体只能用一次!!!
【暴力解法】
物品取或者不取,两种情况,用回溯法去计算,时间复杂度o(2^N)
【动态规划解法】
二维dp数组01背包
动规五部曲!
- 确定dp数组以及下标的含义
(因为有两个变量,物体数 和 背包容量 ,价值是dp数组表达的结果)
dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
- 确定递推公式
两种情况
- 第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]);
- 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]的价值
所以要记住!初始化需要看递推公式需要什么!!!!
- 确定遍历顺序
比较重要的点!
在如下图中,可以看出,有两个遍历的维度:物品与背包重量
遍历的目的是把二维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]);
}
}
- 举例推导
检验一下!
一维dp数组01背包
动规五部曲!
- 确定dp数组以及下标的含义
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
- 一维dp数组的递推公式
还是二维数组一样,加不加当前i物品
加or不加,这是一个question
因此,递推公式为:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
- 一维dp数组如何初始化
关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。
根据递推公式,只需要保证dp[0] = 0 即可
- 一维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]);
}
}
- 举例推导dp数组
举例检验!
完全背包问题
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
分析:
- 背包的容量限定(也可以不是背包 固定大小的容器都可)
- 背包中的物体有两个互相限制的变量,价值和重量 ,背包问题即保证特定重量的前提下,计算出最大价值
- 最重要一点,物体能用无数次!!!
完全背包跟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]);