本题力扣上没有原题,大家可以去卡码网第46题 (opens new window)去练习,题意是一样的。
《代码随想录》算法视频公开课 (opens new window):带你学透0-1背包问题! (opens new window),相信结合视频再看本篇题解,更有助于大家对本题的理解。
这周我们正式开始讲解背包问题!
背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的pdf。
但说实话,背包九讲对于小白来说确实不太友好,看起来还是有点费劲的,而且都是伪代码理解起来也吃力。
对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。
装入背包的最大价值
维度
重量:weight
价值:value
背包容量:j
物品数量:i
1 dp数组的定义和下标的含义
dp[i][j] 物品0~i之间能填入背包j的最大价值
2 确定递推公式
对于物品i 我们只有两种选择
1 若选择物品i 则最大值为 dp[i-1][j-weight[i]]+value[i]
2 若不选择物品i 则最大值为选择上一个物品的最大值 dp[i-1][j]
dp[i][j] =Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
3 dp数组初始化
由于需要 i-1 的值 则需要把第一行初始化:
当j 当j>=weight[i] dp[0][j]=weight[i] 4 确定遍历顺序 先物品后背包 先背包后物品 都可以 5 举例推导dp数组 1 确定dp[i][j]时,若weight[i]>j 则没法取第二种情况,直接取第一种情况dp[i-1][j] 2 行列需要确认好,最好画图 将二维数组压缩成一维数组,只看一维数组中的内容 当选择新的物品时,一维数组中的内容,是上一层一维数组中的所有结果 1 dp数组的定义和下标的含义 dp[j] 物品0~i之间能填入背包j的最大价值 2 确定递推公式 对于物品i 我们只有两种选择 1 若选择物品i 则最大值为 dp[j-weight[i]]+value[i] 2 若不选择物品i 则最大值为选择上一个物品的最大值 dp[j] dp[i][j] =Math.max(dp[j],dp[j-weight[i]]+value[i]); 3 dp数组初始化 只有一行,全为0 4 确定遍历顺序 只能先物品后背包 因为最新一行只能取上一行的内容来确认,要确保每一行都处理完 且只能从后往前遍历:如果从前往后,会把之前的数据打乱,而后续的数据需要依赖前面的数据 5 举例推导dp数组 1 确定dp[j]时,若weight[i]>j 则没法取第二种情况,直接取第一种情况 2 行列需要确认 力扣题目链接(opens new window) 题目难易:中等 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 示例 2: 卡哥说可以用背包,但是我没想到 其实题意就是这个数组能否拆分成总的sum的一半 若总和无法到达一半 ,则无法凑成 若总和可以拆分,题意为:0~i的物品填入sum/2的背包能否填满 1 dp数组的定义和下标的含义 dp[j] 物品0~i之间能填入背包j的最大价值 背包大小sum/2 2 确定递推公式 dp[j] =Math.max(dp[j],dp[j-weight[i]]+value[i]); weight[i] 和 value[i] 都为nums[i] 3 dp数组初始化 都为0 4 确定遍历顺序 先物品后背包 且从后往前 5 举例推导dp数组 1 确定dp[i][j]时,若weight[i]>j 则没法取第二种情况,直接取第一种情况dp[i-1][j] 2 行列需要确认 3 看dp[sum/2] 是否==sum/2自己实现过程中遇到的困难(二维数组)
看到代码随想录之后的想法(滚动数组)
自己实现过程中遇到的困难(滚动数组)
import java.util.*;
public class Main {
//卡哥做法:一维数组
//1 确认dp数组,以及dp数组下标的含义
// dp数组 一维数组 dp[j]
// 相当于讲二维数组进行一个压缩,每次只取最后一行(第i层)
// 一维数组都是利用上一层的结果推出第i层的值
// 下标的含义 任意取物品0~i可存入空间为j的背包的最大值
//2 确定递推公式
// 对于物品i 有两种选择 取两者中的最大值
// 1 取物品i: 则当前i j 的value 为 dp[i-1][j-weight[i]]+value[i]
// 一维数组: dp[j]=dp[j-weight[i]]+value[i]
// 2 不取物品i:则当前 i j 的value 为dp[i-1][j];
// 一维数组: dp[j]=dp[j]
// 若weight[i]>j 则没法取第一种情况,直接取第二种情况
// Math.max(dp[j],dp[j]-weight[j]);
//3 dp数组如何初始化
// dp[j] 的值来自 dp[j] dp[j-weight[i]]
// 若初始化,不能让dp[j]过大,所以统一为最小非负数0
//4 确定遍历顺序
//从后往前,因为j-weight[i] 是需要获取这一行前面的值
//若从前往后,则会打乱上一层留下来的值 无法获取正确的j-weight[i]值
// 按行列都可以
//5 举例推导dp数组
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
int size = sc.nextInt();
int[] value = new int[M];
int[] weight = new int[M];
for(int i = 0; i < M;i++) {
weight[i] = sc.nextInt();
}
for(int i = 0; i < M;i++) {
value[i] = sc.nextInt();
}
int[] dp = new int[size+1];
// 若比weight小则为0 若比weight大则为value
for(int i=0;i<=size;i++){
dp[i]=0;
}
//从i=0开始 也就是第0行就开始赋值
for(int i=0;i
分割等和子集
看到题目的第一想法
看到代码随想录之后的想法(二维数组)
自己实现过程中遇到的困难(二维数组)
class Solution {
public boolean canPartition(int[] nums) {
//背包问题:背包存放的最大价值,看是否选中该物品,在目标背包大小中取最大值
//该问题:背包存放的最大价值,在当前背包大小为target时,看最大价值是否能填满这个target
//两个子集的元素和相等
//其实就是求整个数组的和的一半,看数组中的值能否达到
//转换成背包问题,一个背包 大小为为nums总和的一半,如何相加才能填满
//dp数组下标的含义
//dp数组
//背包的重量 nums
//背包的价值 nums
//不可重复放入
// 确定dp数组的定义和每个下标的含义
// dp[] 为0~i下标填入背包空间j的最大价值,看是否等于11
// 确定递推公式
// dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
// 确定遍历顺序
// 从后往前
// dp数组初始化
// 都为0
// 手动推导dp数组
int sum = 0;
for(int i=0;i