目录
分割等和子集(medium难度)(0-1背包问题,背包问题的基础)
3.2 求方案数的背包问题中:恰好装满(或装到指定容量)对应的初始化
1 0-1背包问题
1.1 问题分析
1.2 算法设计
1.3 完美图解
1.4 伪代码详解
1.5 实战演练
1.6 算法解析和优化拓展
注:上方图片来自陈小玉的《趣学算法》(P210-P220)
重点注意
算法定义部分:
算法优化部分:
1.7 LeetCode上的0-1背包问题
分割等和子集(medium难度)(0-1背包问题,背包问题的基础)
https://leetcode-cn.com/problems/partition-equal-subset-sum/
本题方法思路和代码来源:
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)
参考代码1:
public class Solution {
public boolean canPartition(int[] nums) {
int len = nums.length;
// 题目已经说非空数组,可以不做非空判断
int sum = 0;
for (int num : nums) {
sum += num;
}
// 特判:如果是奇数,就不符合要求
if ((sum & 1) == 1) {
return false;
}
int target = sum / 2;
// 创建二维状态数组,行:物品索引,列:容量(包括 0)
boolean[][] dp = new boolean[len][target + 1];
// 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
if (nums[0] <= target) {
dp[0][nums[0]] = true;
}
// 再填表格后面几行
for (int i = 1; i < len; i++) {
for (int j = 0; j <= target; j++) {
// 直接从上一行先把结果抄下来,然后再修正
dp[i][j] = dp[i - 1][j];
//j恰好等于nums[i],即单独nums[j] 这个数恰好等于此时j(背包的容积)
if (nums[i] == j) {
dp[i][j] = true;
continue;
}
//不选择nums[i],如果在[0, i - 1]这个子区间内已经有一部分元素,使得它们的和为j ,那么dp[i][j] = true;
//选择nums[i],如果在[0, i - 1]这个子区间内就得找到一部分元素,使得它们的和为j - nums[i]。
if (nums[i] < j) {
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
}
}
}
return dp[len - 1][target];
}
}
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)
参考代码2:
public class Solution {
public boolean canPartition(int[] nums) {
int len = nums.length;
int sum = 0;
for (int num : nums) {
sum += num;
}
if ((sum & 1) == 1) {
return false;
}
int target = sum / 2;
boolean[][] dp = new boolean[len][target + 1];
// 初始化成为 true 虽然不符合状态定义,但是从状态转移来说是完全可以的
dp[0][0] = true;
if (nums[0] <= target) {
dp[0][nums[0]] = true;
}
for (int i = 1; i < len; i++) {
for (int j = 0; j <= target; j++) {
dp[i][j] = dp[i - 1][j];
if (nums[i] <= j) {
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
}
}
// 由于状态转移方程的特殊性,提前结束,可以认为是剪枝操作
if (dp[i][target]) {
return true;
}
}
return dp[len - 1][target];
}
}
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)
这里可能会有人困惑为什么压缩到一维时,要采用逆序。因为在一维情况下,是根据
dp[j] || dp[j - nums[i]]
来推d[j]的值,如不逆序,就无法保证在外循环 i 值保持不变 j 值递增的情况下,dp[j - num[i]]的值不会被当前所放入的nums[i]所修改&#