Skip to content
RSS feed tkhwang on GitHub tkhwang on Twitter

πŸ€– leetcode 416. Partition Equal Subset Sum | medium | backtrack | dynamic-programming | javascript

πŸ—’οΈ Problems

Partition Equal Subset Sum - LeetCode

Given a non-empty array nums containing only positive integers,
find if the array can be partitioned into two subsets such
that the sum of elements in both subsets is equal.
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Constraints:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

πŸ”€ πŸ€” First attempt

The first algorithm that I came up with is the backtrack algorithm. But the input size is upto 200. The backtrack algorithm won't work.

var canPartition = function (nums) {
  const total = nums.reduce((a, b) => a + b, 0);
  if (total % 2 === 1) return false;

  const target = total / 2;
  const N = nums.length;

  const dp = (remain, cur, index) => {
    if (remain === 0) {
      if (cur.length < N) return true;
      return false;
    }
    if (remain < 0) return false;

    let local = false;
    for (let i = index; i < N; i += 1) {
      cur.push(nums[i]);
      local = local || dp(remain - nums[i], cur, i + 1);
      cur.pop();
    }
    return local;
  };

  return dp(target, [], 0);
};

λ°”λ‘œ TLE λ°œμƒν•©λ‹ˆλ‹€. 여기에 memo 뢙일 μˆ˜λ„ μžˆκ² μ§€λ§Œ knapsack을 μ΄μš©ν•œ dynamic programming 방법 풀이도 κ°€λŠ₯ν•˜λ‹€κ³  ν•©λ‹ˆλ‹€.

✨ Idea

  • nums[] 합을 반이 λ˜λ„λ‘ ν•˜λŠ” subset 이 μžˆλŠ”κ°€ ?
    • nums[] μ€‘μ—μ„œ μ„ νƒν•΄μ„œ 타켓 무게 (주어진 총합 무게 /2)λ₯Ό λ§Œλ“€ 수 μžˆλŠ”κ°€ ?
  • 01 Knapsack
    • 타켓 무게 : 주어진 무게 / 2
    • 각 물건 무게 : nums[]
    • dp[n][w] : 물건 [1,...,n] μ΄μš©ν•΄μ„œ 무게 w λ₯Ό 꽉 μ±„μšΈ 수 μžˆλŠ”κ°€ ?

πŸ€ Intuition

basecase

λ¬΄κ²Œκ°€ 0이라면 μ•„λ¬΄λŸ° 선택을 ν•˜μ§€ μ•Šμ•„λ„ λͺ©ν‘œ 달성함.

for (let n = 0; n <= N; n += 1) {
  dp[n][0] = true;
}

πŸ•ΈοΈπŸ’‘ μƒνƒœ 전이 방정식

ν˜„μž¬ 선택을 κ²°μ •ν•΄μ•Όν•˜λŠ” nums[n-1] ν•˜λ‚˜λ§ŒμœΌλ‘œ limit 무게 w 보닀 크닀면 선택 결정을 ν•  수 μ—†μœΌλ―€λ‘œ λ°”λ‘œ 이전 결과와 동일함.

if (nums[n - 1] > w) {
  dp[n][w] = dp[n - 1][w];
}

nums[c-1] 선택을 ν•˜λŠ” κ²½μš°μ—λŠ”

  • μ„ νƒν•˜μ§€ μ•Šκ³  이전에 이미 μ„±κ³΅ν•œ 경우 dp[n-1][w]
  • nums[n-1] 을 넣은 경우, nums[n-1] 을 λ„£μ—ˆμœΌλ―€λ‘œ 이 무게λ₯Ό λΊΈ 이전 결과에 λ”°λΌμ„œ κ²°κ³Όκ°€ 결정됨. dp[n-1][w - nums[n-1]]
            } else {
                dp[n][w] = dp[n-1][w] || dp[n-1][w - nums[n-1]];
            }

πŸ”₯β¬†οΈπŸ•ΈοΈ My Solution

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function (nums) {
  const total = nums.reduce((a, b) => a + b, 0);
  if (total % 2 === 1) return false;

  const W = total / 2;

  const N = nums.length;
  const dp = Array(N + 1)
    .fill(null)
    .map((_) => Array(W + 1).fill(false));

  for (let n = 0; n <= N; n += 1) {
    dp[n][0] = true;
  }

  for (let n = 1; n <= N; n += 1) {
    for (let w = 1; w <= W; w += 1) {
      if (nums[n - 1] > w) {
        dp[n][w] = dp[n - 1][w];
      } else {
        dp[n][w] = dp[n - 1][w] || dp[n - 1][w - nums[n - 1]];
      }
    }
  }

  return dp[N][W];
};