You are given an array of positive integers nums.
Return true if you can partition the array into two subsets, subset1 and subset2 where sum(subset1) == sum(subset2). Otherwise, return false.
Example 1:
Input: nums = [1,2,3,4]
Output: trueExplanation: The array can be partitioned as [1, 4] and [2, 3].
Example 2:
Input: nums = [1,2,3,4,5]
Output: falseConstraints:
1 <= nums.length <= 1001 <= nums[i] <= 50
You should aim for a solution as good or better than O(n * t) time and O(n * t) space, where n is the size of the input array and t is half the sum of the array elements.
If the sum of the array elements is not even, we can immediately return false. Think in terms of recursion, where we try to build a subset with a sum equal to half of the total sum. If we find such a subset, the remaining elements will automatically form another subset with the same sum. The entire array can also be considered as one subset, with the other being empty. Can you visualize this as a decision tree to process the array recursively?
We recursively iterate through the array with index i. At each step, we decide whether to include the current element in the subset or not. Instead of forming the subset explicitly, can you think of a better approach? Maybe you only need to track the subset sum rather than generating the subset itself.
We can track the subset sum using a variable curSum. At each step, we make two recursive calls. If adding the current element does not exceed the target, we include it. If either path leads to a solution, we immediately return true. Can you determine the base case for this recursion? All elements in the array are positive.
If curSum equals half the sum of the array elements, we return true. If index i goes out of bounds, we return false. This solution is exponential, but we can use memoization to cache recursive call results and avoid redundant computations. We can use a hash map or a 2D array with dimensions n * t, where n is the size of the input array and t is half the sum of the input array elements.
The problem asks whether we can split the array into two subsets with equal sum.
Key observation:
False.Can we pick a subset whose sum is
totalSum / 2?
This is a classic subset sum decision problem.
Using recursion:
sum/2) until:0 → successtotalSum = sum(nums)totalSum is odd, return Falsetarget = totalSum // 2dfs(i, target):target == 0, return Truei == len(nums) or target < 0, return Falsenums[i]nums[i] (reduce target)dfs(0, target)class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2:
return False
def dfs(i, target):
if i >= len(nums):
return target == 0
if target < 0:
return False
return dfs(i + 1, target) or dfs(i + 1, target - nums[i])
return dfs(0, sum(nums) // 2)This is the same subset sum idea as the recursive approach, but optimized using memoization.
Key observations:
totalSum / 2.In plain recursion, many subproblems repeat:
itargetTo avoid recomputing them, we store results in a DP table:
memo[i][t] = whether it’s possible to form sum t using elements from index i onward.This turns the exponential recursion into a polynomial-time solution.
total = sum(nums)total is odd, return Falsetarget = total // 2memo[n][target + 1] initialized to -1dfs(i, target):target == 0, return Truei == n or target < 0, return Falsememo, return itdfs(i + 1, target)dfs(i + 1, target - nums[i])dfs(0, target)class Solution:
def canPartition(self, nums: List[int]) -> bool:
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
n = len(nums)
memo = [[-1] * (target + 1) for _ in range(n + 1)]
def dfs(i, target):
if target == 0:
return True
if i >= n or target < 0:
return False
if memo[i][target] != -1:
return memo[i][target]
memo[i][target] = (dfs(i + 1, target) or
dfs(i + 1, target - nums[i]))
return memo[i][target]
return dfs(0, target)Where is the length of the array and is the sum of array elements divided by 2.
This is a classic 0/1 subset sum DP.
We want to split nums into two subsets with equal sum. That’s only possible if:
total = sum(nums) is eventarget = total / 2Instead of trying all subsets (exponential), we build the answer gradually:
dp[i][j] mean: using the first i numbers, can we form sum j?For each number, we have two choices:
dp[i-1][j]nums[i-1] <= j) → check dp[i-1][j - nums[i-1]]If either is true, then dp[i][j] is true.
total = sum(nums). If total is odd, return False.target = total // 2, n = len(nums).dp of size (n+1) x (target+1) filled with False.dp[i][0] = True for all i (sum 0 is always achievable by taking nothing).i from 1..n:j from 1..target:nums[i-1] <= j:dp[i][j] = dp[i-1][j] OR dp[i-1][j - nums[i-1]]dp[i][j] = dp[i-1][j]dp[n][target].class Solution:
def canPartition(self, nums: List[int]) -> bool:
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
n = len(nums)
dp = [[False] * (target + 1) for _ in range(n + 1)]
for i in range(n + 1):
dp[i][0] = True
for i in range(1, n + 1):
for j in range(1, target + 1):
if nums[i - 1] <= j:
dp[i][j] = (dp[i - 1][j] or
dp[i - 1][j - nums[i - 1]])
else:
dp[i][j] = dp[i - 1][j]
return dp[n][target]Where is the length of the array and is the sum of array elements divided by 2.
This is the same subset sum idea as before, but optimized to use 1D DP.
Instead of keeping a full 2D table (dp[i][j]), we only track:
dp[j] → whether sum j is achievable using numbers processed so farAt each number, we build a new state (nextDp) from the previous one:
dp[j]dp[j - nums[i]]This works because each state only depends on the previous row.
total = sum(nums). If total is odd, return False.target = total // 2.dp of size target + 1.dp[0] = True (sum 0 is always possible).num in nums:nextDp.j from 1 to target:j >= num:nextDp[j] = dp[j] OR dp[j - num]nextDp[j] = dp[j]dp with nextDp.dp[target].class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2:
return False
target = sum(nums) // 2
dp = [False] * (target + 1)
nextDp = [False] * (target + 1)
dp[0] = True
for i in range(len(nums)):
for j in range(1, target + 1):
if j >= nums[i]:
nextDp[j] = dp[j] or dp[j - nums[i]]
else:
nextDp[j] = dp[j]
dp, nextDp = nextDp, dp
return dp[target]Where is the length of the array and is the sum of array elements divided by 2.
This approach also solves Partition Equal Subset Sum, but instead of arrays, it uses a Hash Set to track all achievable sums.
At any point:
dp contains all subset sums that can be formed using the processed numbers.For each new number:
t can either:t + num (pick the number)If at any time we form target, we can stop early and return True.
total = sum(nums). If total is odd, return False.target = total // 2.dp = {0} (sum 0 is always possible).nextDP.t in dp:t + num == target, return True.t to nextDP (skip current number).t + num to nextDP (take current number).dp with nextDP.target, return False.class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2:
return False
dp = set()
dp.add(0)
target = sum(nums) // 2
for i in range(len(nums) - 1, -1, -1):
nextDP = set()
for t in dp:
if (t + nums[i]) == target:
return True
nextDP.add(t + nums[i])
nextDP.add(t)
dp = nextDP
return FalseWhere is the length of the array and is the sum of array elements divided by 2.
This is the most optimal DP solution for Partition Equal Subset Sum.
We reduce the problem to:
Can we pick some numbers whose sum equals
target = total_sum / 2?
We use a 1D DP array where:
dp[j] = True means we can form sum j using some of the numbers so far.Key idea:
total = sum(nums). If total is odd, return False.target = total // 2.dp of size target + 1.dp[0] = True (sum 0 is always achievable).num in nums:j from target down to num:dp[j] = dp[j] OR dp[j - num]dp[target].class Solution:
def canPartition(self, nums: list[int]) -> bool:
if sum(nums) % 2:
return False
target = sum(nums) // 2
dp = [False] * (target + 1)
dp[0] = True
for num in nums:
for j in range(target, num - 1, -1):
dp[j] = dp[j] or dp[j - num]
return dp[target]Where is the length of the array and is the sum of array elements divided by 2.
class Solution:
def canPartition(self, nums: list[int]) -> bool:
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
dp = 1 << 0
for num in nums:
dp |= dp << num
return (dp & (1 << target)) != 0Where is the length of the array and is the sum of array elements divided by 2.