Description
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
Solution 01背包
这里注意01背包当前物品只能使用一次:
dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]]
如果是完全背包就得这么写了:
dp[i][j] = dp[i-1][j] or dp[i][j-nums[i]]
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) & 1: return False
target = sum(nums) // 2
dp = [[0]*(target + 1) for _ in range(len(nums) + 1)]
for i in range(len(nums) + 1):
dp[i][0] = 1 # base case
for i in range(len(nums)):
for j in range(1, target+1):
if j - nums[i] >= 0:
# 01背包当前nums[i]只能使用一次
dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]]
else:
dp[i][j] = dp[i-1][j]
return True if dp[len(nums)-1][target] else False
Solution
class Solution:
def canPartition(self, nums):
if sum(nums) & 1:
return False
# [1, 5, 11, 5]为例子
# 问题可以看成背包大小为(1+5+11+5)/2
# 第一额物品占用背包空间为1,价值为1
# 最后检查背包满了之后价值是否是(1+5+11+5)/2
# 注意初始化 要求恰好装满除了dp[0]其他需置非法值
dp = [-1 for _ in range(sum(nums)//2+1)]
dp[0] = 0
target = sum(nums)//2
C = nums
V = nums
n = len(V)
def ZeroOneBackPack(cost, value):
for i in range(target, cost-1, -1):
dp[i] = max(dp[i], dp[i-cost]+value)
for i in range(n):
ZeroOneBackPack(C[i], V[i])
if dp[target] == sum(nums)//2:
return True
else:
return False