Problem
Given an array of positive integers, determine whether you can split it into two non-empty groups such that both groups have the same sum. Every element must go to exactly one group.
Example:
- Input:
nums = [1, 5, 11, 5] - Output:
true - Explanation:
{11}and{1, 5, 5}each sum to 11.
Counter-example: nums = [1, 2, 3, 5] has total sum 11, which is odd โ no equal split is possible, so the answer is false.
Intuition
If two groups have equal sums, each must add up to exactly half the array's total โ so the first thing to check is whether that total is even. From there, the problem becomes: is there any subset that hits exactly total / 2? This is the 0/1 knapsack pattern: each element is either included or skipped, and we track every reachable sum as we consider elements one by one.
Approach 1 โ 2D DP Table
Build a table where dp[i][j] is true if we can reach sum j using only the first i elements. For each new element, either skip it (copy the row above) or include it (look back by that element's value).
Steps:
- Compute
target = total // 2; returnFalseimmediately iftotalis odd. - Create a
(n+1) ร (target+1)table initialized toFalse; setdp[i][0] = Truefor alli(empty subset always reaches sum 0). - For each element index
i(1-based) and each candidate sumj:- Carry forward
dp[i-1][j](skip this element). - If
nums[i-1] <= j, also OR indp[i-1][j - nums[i-1]](include this element).
- Carry forward
- Return
dp[n][target].
1def canPartition(nums: list) -> bool:
2 total = sum(nums)
3 if total % 2 != 0:
4 return False
5 target = total // 2
6 n = len(nums)
7 dp = [[False] * (target + 1) for _ in range(n + 1)]
8 for i in range(n + 1):
9 dp[i][0] = True # empty subset always reaches sum 0
10 for i in range(1, n + 1):
11 for j in range(1, target + 1):
12 dp[i][j] = dp[i - 1][j] # skip current element
13 if nums[i - 1] <= j:
14 dp[i][j] |= dp[i - 1][j - nums[i - 1]] # include current element
15 return dp[n][target]Time: O(n ร target) โ fill an n ร target table once.
Space: O(n ร target) โ the full 2D boolean table.
Approach 2 โ 1D DP (Space-Optimized)
Each row of the 2D table only depends on the row above, so we can collapse to a single array dp[j] meaning "can we reach sum j with the elements seen so far?" The key is iterating j from high to low so each element is used at most once.
Steps:
- Compute
target = total // 2; returnFalseiftotalis odd. - Initialize
dp[0..target]toFalse, withdp[0] = True. - For each
numinnums, iteratejfromtargetdown tonum:dp[j] |= dp[j - num]
- Return
dp[target].
1def canPartition(nums: list) -> bool:
2 total = sum(nums)
3 if total % 2 != 0:
4 return False
5 target = total // 2
6 dp = [False] * (target + 1)
7 dp[0] = True # zero sum is always reachable
8 for num in nums:
9 for j in range(target, num - 1, -1): # high-to-low prevents reusing the same element
10 dp[j] |= dp[j - num]
11 return dp[target]Time: O(n ร target) โ same traversal, one row.
Space: O(target) โ single 1D boolean array instead of the full table.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| 2D DP Table | O(n ร target) | O(n ร target) | When you want an explicit table for tracing or debugging the recurrence |
| 1D DP (space-optimized) | O(n ร target) | O(target) | In practice โ the memory savings are significant when target is large |
Common Mistakes
- Skipping the parity check: If
totalis odd, no equal split exists. Without this guard you'll silently get a wrong answer or an out-of-bounds error. - Iterating forward in the 1D DP: Looping
jfromnumup totargetinstead of downward lets the same element be used multiple times, incorrectly converting this into an unbounded knapsack. - Not short-circuiting when an element equals the target: If any single element equals
target, you can returnTrueimmediately โ that element alone forms one half. - Off-by-one in the inner loop bounds: The reverse loop must reach
j == num(Python:range(target, num - 1, -1)) so thatdp[num - num] = dp[0] = Truepropagates intodp[num]. - Integer overflow when summing: In languages with fixed-width integers, summing a large array before the parity check can overflow. Compute the total in a wider type or check element bounds first.
Related Problems
target-sumโ assign+orโto each element to reach a target; reduces to a subset sum problem with the same DP structurecoin-changeโ find minimum coins to reach a target; the unbounded-knapsack sibling where elements can repeatsubsets-iiโ enumerate all subsets with duplicates; explores the same include/skip decision tree the DP models implicitlyword-breakโ can a string be segmented using dictionary words; another boolean reachability DP with a near-identical structureunique-pathsโ count grid paths; a DP table that shares the same 2D โ 1D compression technique shown in Approach 2