AlgoMaster Logo

Partition Equal Subset Sum

mediumFrequency7 min readUpdated June 23, 2026

Understanding the Problem

We need to decide whether it is possible to split the array into two groups where both groups have the same total. If both subsets have equal sum, then each subset's sum must be exactly half of the total array sum. If the total sum is odd, there is no way to split it evenly, so we return false right away.

Once we know the target is totalSum / 2, the problem becomes: can we find a subset of nums that adds up to exactly target? This is the classic 0/1 Knapsack / Subset Sum problem. Each number is either included in the subset or it is not, and we want to know if any combination of included numbers sums to the target exactly.

Key Constraints:

  • 1 <= nums[i] <= 100 and 1 <= nums.length <= 200 → The total sum is at most 200 100 = 20,000, so the target (sum / 2) is at most 10,000. An O(n target) DP table holds at most 200 * 10,000 = 2 million entries, which is the bound that makes the polynomial approaches feasible.
  • All values are positive, with no zeros or negatives. A sum reached during the DP only grows as elements are added, which is why the target is a fixed upper bound and the subset-sum framing applies directly.

Approach 1: Brute Force (Recursion)

Intuition

Try every possible subset. For each element, we have two choices: include it in the subset or exclude it. If any combination of included elements sums to the target (totalSum / 2), we have found a valid partition.

This is a depth-first search through the decision tree. At each level, we pick one element and branch into "take it" or "leave it." If a branch reduces the remaining sum to exactly zero, we return true. Otherwise, we backtrack and try the other option.

Algorithm

  1. Compute the total sum of the array. If it is odd, return false immediately.
  2. Set target = totalSum / 2.
  3. Define a recursive function canFind(nums, index, remaining) that returns true if we can form a sum of remaining using elements from index index onward.
  4. Base cases: if remaining == 0, return true. If remaining < 0 or index >= nums.length, return false.
  5. Recurse: try including nums[index] (subtract it from remaining) or excluding it (keep remaining the same).

Example Walkthrough

1Start: canFind(index=0, remaining=11)
0
1
index=0
1
5
2
11
3
5
1/6

Code

The brute force explores up to 2^n subsets, which is far too slow for n = 200. Many recursive calls solve the same subproblem repeatedly. The next approach caches each result so the same (index, remaining) pair is never solved twice.

Approach 2: Top-Down DP (Memoization)

Intuition

The brute force recomputes the same subproblems many times. The state of each recursive call is fully described by two values: the current index and the remaining sum we need to reach. Once we have computed whether it is possible to form remaining from elements at positions index through n-1, that answer never changes, so we can store it and reuse it.

We keep a 2D memo table keyed by (index, remaining). Before doing any work, we check whether this state has been computed. If so, return the cached answer. If not, compute it, store it, and return.

Algorithm

  1. Compute total sum. If odd, return false. Set target = totalSum / 2.
  2. Create a memo table (2D array or hash map) initialized to "unvisited."
  3. Define canFind(index, remaining):
    • If remaining == 0, return true.
    • If remaining < 0 or index >= n, return false.
    • If memo[index][remaining] is already computed, return it.
    • Compute result by trying include/exclude, store in memo, and return.
  4. Return canFind(0, target).

Example Walkthrough

1Start: canFind(index=0, remaining=11)
0
1
index=0
1
5
2
11
3
5
1/6

Code

The memo table uses O(n * target) space, but each row only depends on the row below it, so we store n rows when one would do. The next approach builds the table bottom-up and reduces it to a single 1D array.

Approach 3: Bottom-Up DP (1D Array)

Intuition

Instead of working top-down with recursion, we build the solution bottom-up. We define dp[j] as a boolean: can we form sum j using some subset of the elements processed so far?

We start with dp[0] = true, since sum 0 is reachable by taking nothing. Then for each number in the array, we update the DP array: if sum j - num was already reachable, then including num makes sum j reachable too.

The update iterates from right to left, from target down to num. Left-to-right iteration would let a single element be counted more than once: setting dp[j] from dp[j - num] and then, later in the same pass, setting dp[j + num] from the freshly updated dp[j], which reuses num. Right-to-left iteration reads dp[j - num] before it can be overwritten in this pass, so it reflects the state before num was added, and each element contributes at most once.

Algorithm

  1. Compute total sum. If odd, return false. Set target = totalSum / 2.
  2. Create a boolean array dp of size target + 1, initialized to false. Set dp[0] = true.
  3. For each number num in the array:
    • For j from target down to num:
      • If dp[j - num] is true, set dp[j] = true.
  4. Return dp[target].

Example Walkthrough

1Initial: dp[0]=true (sum 0 is always reachable)
[T, F, F, F, F, F, F, F, F, F, F, F]
1/7

Code