Skip to content

5.分割等和子集

思路

  • 背包的体积为 sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如何正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入。

题解

::: code-tabs#js

@tab 二维数组解法

js
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    const sum = nums.reduce((a, b) => a + b)
    if (sum % 2 !== 0) return false
    const num = sum / 2
    const dp = new Array(nums.length).fill().map(() => new Array(num + 1).fill(0))
    for (let i = nums[0];i <= num;i++) {
        dp[0][i] = nums[0];
    }
    for (let i = 1;i < nums.length;i++) {
        for (let j = 0;j <= num;j++) {
            // 背包容量没有物品大
            if (j < nums[i]) {
                dp[i][j] = dp[i - 1][j]
            } else {
                dp[i][j] = Math.max(dp[i - 1][j - nums[i]] + nums[i], dp[i - 1][j])
            }
        }
    }
    return dp[nums.length - 1][num] === num
};
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    const sum = nums.reduce((a, b) => a + b)
    if (sum % 2 !== 0) return false
    const num = sum / 2
    const dp = new Array(nums.length).fill().map(() => new Array(num + 1).fill(0))
    for (let i = nums[0];i <= num;i++) {
        dp[0][i] = nums[0];
    }
    for (let i = 1;i < nums.length;i++) {
        for (let j = 0;j <= num;j++) {
            // 背包容量没有物品大
            if (j < nums[i]) {
                dp[i][j] = dp[i - 1][j]
            } else {
                dp[i][j] = Math.max(dp[i - 1][j - nums[i]] + nums[i], dp[i - 1][j])
            }
        }
    }
    return dp[nums.length - 1][num] === num
};

@tab 滚动数组解法

js
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function (nums) {
  const sum = nums.reduce((a, b) => a + b);
  // 如果不是2的整数 直接返回false
  if (sum & 1) return false;

  // 背包容量最大为数组总和的一半
  const target = sum / 2;
  const dp = new Array(target + 1).fill(0);

  // 遍历数组
  for (let i = 0; i < nums.length; i++) {
    // 倒序遍历背包容量
    for (let j = target; j >= nums[i]; j--) {
      dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
    }
  }
  return dp[target] === target;
};
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function (nums) {
  const sum = nums.reduce((a, b) => a + b);
  // 如果不是2的整数 直接返回false
  if (sum & 1) return false;

  // 背包容量最大为数组总和的一半
  const target = sum / 2;
  const dp = new Array(target + 1).fill(0);

  // 遍历数组
  for (let i = 0; i < nums.length; i++) {
    // 倒序遍历背包容量
    for (let j = target; j >= nums[i]; j--) {
      dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
    }
  }
  return dp[target] === target;
};

:::