题目
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
解题思路
用dp[i][j]
表示选择nums
前i
个数字,能组成和为j
的情况,则状态转移为:dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
,后面成立的条件是j - nums[i] >= 0
因为dp只和j
有关,所以可以缩减空间,因为每个数值是从上一行同列、上一行左边的列转移而来,所以更新的时候从右向左更新
时间复杂度 o ( l e n ( n u m s ) ∗ [ s u m ( n u m s ) / / 2 ] ) o(len(nums) * [sum(nums) // 2]) o(len(nums)∗[sum(nums)//2])
代码
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) & 1 == 1:
return False
dp = [[False] * (sum(nums) // 2 + 1) for _ in range(len(nums))]
# init
for row in range(len(dp)):
dp[row][0] = True
# dp
for row in range(len(dp)):
for col in range(1, len(dp[0])):
dp[row][col] = dp[row - 1][col] or (dp[row - 1][col - nums[row]] if col - nums[row] >= 0 else False)
return dp[-1][-1]
缩减空间版:
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) & 1 == 1:
return False
dp = [False] * (sum(nums) // 2 + 1)
# init
dp[0] = True
# dp
for row in range(len(nums)):
for col in range(len(dp) - 1, -1, -1):
dp[col] = dp[col] or (dp[col - nums[row]] if col - nums[row] >= 0 else False)
return dp[-1]