416. 分割等和子集
在若干物品中选出一些物品,每个物品只能使用一次,这些物品恰好能够填满容量为 sum/2 的背包。
**动态规划思路:**一个一个物品去尝试,一点一点扩大考虑能够容纳的容积的大小。整个过程就像在填写一张二维表格
-
确定dp数组(dp table)以及下标的含义
dp[i][j]: 表示考虑下标 [0, i] 这个区间里的所有正数,在他们当中是否能够选出一些数,使得这些书之和恰好为正数 j
-
确定递推公式
不选择 nums[i]:
dp[i][j] = dp[i - 1][j]
选择 nums[i]:
dp[i][j] = dp[i - 1][j] || dp[i][j - nums[j]]
-
dp数组如何初始化
如果能够分割成等和子集,则说明 nums 中所有元素和一定为偶数,否则不可能分割成等和子集。
创建二维数组,
[nums.length][sum/2]
,将初始值先置为false
初始化 dp 首行,当然也要防止下标越界
if (nums[0] <= target) { dp[0][nums[0]] = true; }
-
确定遍历顺序
首先遍历
nums
数组,再遍历目标整数颠倒顺序不受影响
-
举例推导dp数组
/**
* @param {number[]} nums
* @return {boolean}
*/
var canPartition = function (nums) {
let len = nums.length;
let sum = 0;
for (let val of nums) sum += val;
if (sum & 1) {
return false;
}
let target = sum / 2;
let dp = new Array(len).fill().map(item => new Array(target + 1).fill(false));
if (nums[0] <= target) {
dp[0][nums[0]] = true;
}
for (let i = 1; i < len; i++) {
for (let j = 0; j <= target; j++) {
dp[i][j] = dp[i - 1][j]
if (nums[i] === j) {
dp[i][j] = true;
continue;
}
if (nums[i] < j) {
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]
}
}
}
// 谨记 dp[i][j] 为 [0,1] 区间中选取一些数,使得这些数的和恰好为整数 j
return dp[len - 1][target];
};
/**
* @param {number[]} nums
* @return {boolean}
*/
var canPartition = function (nums) {
let len = nums.length;
let sum = 0;
for (let val of nums) sum += val;
if (sum & 1) {
return false;
}
let target = sum / 2;
let dp = new Array(len).fill().map(item => new Array(target + 1).fill(false));
dp[0][0] = true;
if (nums[0] <= target) {
dp[0][nums[0]] = true;
}
for (let i = 1; i < len; i++) {
for (let 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];
};
一维滚动数组优化
/**
* @param {number[]} nums
* @return {boolean}
*/
var canPartition = function (nums) {
let len = nums.length;
let sum = 0;
for (let val of nums) sum += val;
if (sum & 1) {
return false;
}
let target = sum / 2;
let dp = new Array(target + 1).fill(false);
dp[0] = true;
if (nums[0] <= target) {
dp[0][nums[0]] = true;
}
for (let i = 1; i < len; i++) {
for (let j = target; j >= 0 && nums[i] <= j; j--) {
dp[j] = dp[j] || dp[j - nums[i]]
}
if (dp[target]) return true;
}
return dp[target];
};