The problem
Given an array nums of positive integers, return True if it can be split into two subsets with equal sums, otherwise False. Every element goes into exactly one of the two subsets.
Input: nums = [1, 5, 11, 5]
Output: True
Input: nums = [1, 2, 3, 5]
Output: False[1, 5, 5] and [11] both sum to 11. For [1, 2, 3, 5] the total is 11, which is odd, so no equal split exists.
How to think about it
Start with brute force. Try every way to assign each number to side A or side B: O(2ⁿ). With up to 200 numbers that is hopeless.
First reduction: you only need one side. If the two halves are equal, each sums to total / 2. So the question becomes “is there a subset that sums to target = total / 2?” If total is odd, the answer is immediately False. The rest of the numbers automatically form the other half.
The key insight: track which sums are reachable. Process the numbers one at a time and keep the set of sums you can make from the numbers seen so far. Each new number x either stays out (every old sum is still reachable) or joins (every old sum t gives a new sum t + x). That is the 0/1 knapsack choice, and the set never holds more than target + 1 values because sums above target are useless.
Which pattern is this? 0/1 knapsack, as a 1-D DP over sums. The tell is “split into two equal groups” or “pick a subset hitting a target”, where each item is used at most once.
Approach
- Compute
total. If it’s odd, returnFalse. - Set
target = total // 2anddp = {0}(the empty subset). - For each
x: addx + ttodpfor everytalready indp, keeping only sums<= target. - Return whether
targetis indp.
Solution
def can_partition(nums):
total = sum(nums)
if total % 2: return False # odd total can't split evenly
target = total // 2; dp = {0} # dp = sums reachable so far
for x in nums:
dp |= {x + t for t in dp if x + t <= target} # x joins each old sum (or stays out)
return target in dp
# can_partition([1,5,11,5]) -> True ; can_partition([1,2,3,5]) -> FalseThe set comprehension is built from the old dp before |= merges it, so each number is used at most once.
Dry run
On [1, 5, 11, 5]: total = 22, target = 11.
x |
reachable sums after x |
|---|---|
| (start) | {0} |
| 1 | {0, 1} |
| 5 | {0, 1, 5, 6} |
| 11 | {0, 1, 5, 6, 11} |
| 5 | {0, 1, 5, 6, 10, 11} |
11 is reachable, so the answer is True.
Complexity
- Time: O(n · target). Each number touches at most
target + 1sums. - Space: O(target). The set of reachable sums. A boolean array
dp[0..target]iterated from high to low works the same way.
Edge cases and pitfalls
- Odd total returns
Falsebefore any DP. - Single element (
[2]) returnsFalse: one side would be empty with sum 0. - With a boolean array, iterate sums downwards. Going upwards lets the same number be added twice (that’s unbounded knapsack, as in Coin Change II).
- Early exit: you can return
Truethe momenttargetentersdp.
Related problems
- Partition to K Equal Sum Subsets (LeetCode 698): the generalisation to
kgroups, which needs backtracking or bitmask DP. - Coin Change II (LeetCode 518): the unbounded version, where each item can be reused.
- Subsets (LeetCode 78): the brute force this DP replaces, listing every subset.
- 1.Climbing Stairs (LeetCode 70)
- 2.Min Cost Climbing Stairs (LeetCode 746)
- 3.Maximum Subarray (LeetCode 53)
- 4.House Robber (LeetCode 198)
- 5.House Robber II (LeetCode 213)
- 6.Jump Game (LeetCode 55)
- 7.Coin Change (LeetCode 322)
- 8.Coin Change II (LeetCode 518)
- 9.Word Break (LeetCode 139)
- 10.Decode Ways (LeetCode 91)
- 11.Maximum Product Subarray (LeetCode 152)
- 12.Longest Increasing Subsequence (LeetCode 300)
- 13.Partition Equal Subset Sum (LeetCode 416)