背包问题
背包问题:
- 背包:最大容量v
- 物品:
- 物品价值w
- 物品体积v
- 每个物品的数量
- 只有一个(01背包)
- 无数个(完全背包)
例子:
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
背包最大重量为4。
物品为:
重量 | 价值 | |
---|---|---|
物品0 | 1 | 15 |
物品1 | 3 | 20 |
物品2 | 4 | 30 |
问背包能背的物品最大价值是多少?
1、01背包入门
1.1 二维背包
/**
* 背包最大重量为4
*
* 重量 价值
* 物品0 1 15
* 物品1 3 20
* 物品2 4 30
*
*
*/
/**
* 思路: 二维dp数组01背包
*
* 1. 确定dp数组以及下标含义
* dp[i][j]: 表示从下标为0~i的物品任意取,放进容量为j的背包,价值总和最大是多少
*
* 2. 确定递推公式
* if(j < weight[i]) {
* dp[i][j] = dp[i-1][j];
* } else {
* dp[i][j] = Math.max(dp[i-1][j-weight[i]] + value[i], dp[i-1][j])
* 放入物品i 不放入物品i
* }
*
* 3. 初始化
* 背包容量为0时, 即j = 0 时, dp[i][0] = 0
*
* i = 0时
* for(int j = weight[0]; j <= bagweight; j++) {
* dp[0][j] = value[0];
* }
*
* 4. 确定遍历顺序
* 先遍历物品,再遍历背包容量; 反之也可以
* 从上到下,从左往右
*
* 5. 举例推导dp数组
* 重量
* 0 1 2 3 4
* 物品0 0 15 15 15 15
* 物品1 0 15 15 20 35
* 物品2 0 15 15 20 35
*
*
* 时间: O(n*m)
* 空间: O(n*m)
* n为物品,m为重量
*/
static void wei_bag_problem() {
int[] weight = {
1,3,4};
int[] value = {
15,20,30};
int bagWeight = 4;
// 1. 确定dp数组以及下标含义
// 二维数组
// dp[i][j] 代表下标[0-i]物品里任意取,放进容量为j的背包,价值总和最大是多少
int[][] dp = new int[weight.length][bagWeight + 1];
// 2. 初始化
// 背包重量为0的 dp[i][0]价值为0
for (int i = 0; i < weight.length; i++) {
dp[i][0] = 0;
}
// 存放编号为0的物品的时候, 各个容量的背包所能存放的最大价值
// 倒叙遍历
/*for (int j = bagWeight; j >= weight[0]; j--) {
dp[0][j] = dp[0][j - weight[0]] + value[0];
}*/
// 正序遍历
for (int j = weight[0]; j <= bagWeight; j++) {
dp[0][j] = value[0];
}
// 3. 遍历顺序
for (int i = 1; i < weight.length; i++) {
// 遍历物品
for (int j = 1; j <= bagWeight; j++) {
// 遍历背包容量
if(j < weight[i]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
}
}
}
System.out.println(dp[weight.length-1][bagWeight]);
}
1.2 一维背包
public class _01_背包_一维 {
/**
* 背包最大重量为4
*
* 重量 价值
* 物品0 1 15
* 物品1 3 20
* 物品2 4 30
*
*
*/
/**
* 思路: 一维数组01背包
*
* 1. 确定dp数组以及下标含义
* dp[j]: 表示容量为j的背包的最大价值
*
* 2. 确定递推公式
* dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i])
* 不放入当前物品 放入当前物品
*
* 3. 初始化
* dp[0] = 0
*
* 4. 确定遍历顺序
* 先遍历物品,再遍历背包容量
* 遍历背包容量的时候,应该是倒叙遍历
*
* 如果是正序遍历的话
* dp[1] = dp[1-weight[0]] + value[0] = 15
* dp[2] = dp[2-weight[0]] + value[0] = 15 + 15 = 30
* 重复放了2次
*
* 如果是倒叙遍历的话
* dp[2] = dp[2-weight[0]] + value[0] = 15
* dp[1] = dp[1-weight[0]] + value[0] = 15
*
* 5. 举例推导递推公式
* 背包容量: 0 1 2 3 4
* 用物品0,遍历背包: 0 15 15 15 15
* 用物品1,遍历背包: 0 15 15 20 35
* 用物品2,遍历背包: 0 15 15 20 35
*/
public static void wei_bag_problem() {
int[] weight = {
1,3,4};
int[] value = {
15, 20, 30};
int bagweight = 4;
// 1. 确定dp数组及下标含义
// dp[j] 表示容量为j的背包,价值总和最大是多少
int[] dp = new int[bagweight+1];
// 2. 确定递推公式
// dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]) 不放入物品, 放入物品
// 所以一维数组时,递推公式如下
// dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i])
// 3. 初始化
// 要和dp数组的定义吻合
// 若价值都为正整数,则全部初始化为0, 如果出