Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.
solutions
recursion w/ memoization
def canPartition(self, nums: List[int]) -> bool:
s = sum(nums)
if s & 1:
return False
target = s // 2
memo = {}
def recurse(idx, acc):
nonlocal target
if acc == target:
return True
if idx == len(nums) or acc > target:
return False
if (idx, acc) in memo:
return memo[(idx, acc)]
res = False
# take
res |= recurse(idx+1, acc+nums[idx])
# skip
res |= recurse(idx+1, acc)
memo[(idx, acc)] = res
return res
return recurse(0, 0)dp
- to see if the array can be divided into two equal subsets, we just need to make sure that a subset of the array can sum to half of the total sum of the array.
- to do this, we can do it either top-down or bottom-up with dynamic-programming.
- when doing top-down, we can just use recursion with memoization.
- with bottom-up, this problem is very similar to the knapsack problem.
- at each value in
nums, we can choose to either use it or not use it. - working up the possible
sumvalues from 0 to half of the total sum, we know that the sum ofjcan be created using the numbers innums[:i]with two cases: (dp[i][j], wherejis the current sum)- not using the current value
nums[i], we can just look atdp[i-1][j]. - using the current value, we look at
dp[i-1][j-nums[i]](only ifnums[i]is .
- not using the current value
- for my actual implementation, i just store the previous row of the
dpinstead of everything, for memory purposes.
- at each value in
def canPartition(self, nums: List[int]) -> bool:
s = sum(nums)
if s & 1:
return False
target = s // 2
# sum of 0 is trivially doable
dp = [[True]+[False]*target for _ in range(len(nums)+1)]
for i in range(1, len(nums)+1):
for j in range(1, target+1):
skip = dp[i-1][j]
take = False
if j >= nums[i-1]:
take = dp[i-1][j-nums[i-1]]
dp[i][j] = skip or take
return dp[-1][-1]