解1:二维dp数组,时间O(m*n),空间O(m*n),m、n为dp数组的行和列数。
判断原数组总和能否整除2;
将target设为total // 2(若是total / 2,target为float,需要转为int才能以target为维度新建dp数组);
* 新建dp,行数 = nums长度,列数 = target+1,i行j列dp[i][j]表示从子数组nums[0:i+1](第0~第i个数)不重复地取数,能够获得的不超过j的最大总和。dp[len(nums)-1][target]若等于target,说明在整个nums中能够不重复地取出总和恰好为target的数,返回true。
* 考虑dp[i][j]的计算:若当前数nums[i] > j,说明i不可加入,于是dp[i][j] = dp[i-1][j]。否则,i可能加入,获得总和 = dp[i-1][j-nums[i]] + nums[i](将加入了i数的背包想象成两部分,一部分是i数本身,价值和重量都为i,另一部分不含i而且重量上限为j-nums[i],dp[i-1][j-nums[i]]就是第二部分能取到的最大价值);i也可以选择不加入,获得总和 = dp[i-1][j]。用i加入/不加入中的较大者更新dp[i][j]。
由此可见,需要通过dp[i][j]左侧及上侧的值计算它。遍历时从左到右,从上到下。
* base情况,考虑左、上边缘的值:1)dp[x][0]表示总和上限=0,因此dp[x][j]=0(dp本来都初始化为0,不需处理);2)dp[0][x]表示只考虑nums第0个数,则当x小于nums[0],初始为0,当x不小于nums[0],都初始化为nums[0]。
遍历每行、每列,直到右下角。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if len(nums) <= 1:
return False
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
#dp[i][j]:从nums的[0, i]中取数,能获得的不超过j的最大总和.dp: len(nums) * (target+1)
dp = [[0 for _ in range(target+1)] for _ in range(len(nums))]
#转移:dp[i][j] = max(dp[i-1][j], dp[i][j-nums[i]]+nums[i])
#base:不超过0的最大总和:只能不取任何数,dp[x][0] = 0,
# 只考虑第0个数时:dp[0][x] = nums[0] if x >= nums[0] else = 0
for x in range(nums[0], target+1):
dp[0][x] = nums[0]
for j in range(1, target+1):#最大和=j
for i in range(1, len(nums)):#取0~i个数
if nums[i] > j:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i])
return dp[-1][-1] == target
解2:压缩为一维dp数组,时间O(m*n),空间O(n)
将物品/nums[i]维度压缩掉了,dp是长度=target+1的一维数组,按nums[0]的情况初始化dp。由于新的dp[j]需要参考“上一行左侧”或“上一行正上方”的值,应该从后往前遍历dp,防止上一行的值在使用之前被新的值覆盖。若当前数nums[i]超过当前上限j,不改变dp[j]的值(相等于沿用上一行正上方的值);否则,用当前值dp[j]与dp[j-nums[i]] + nums[i]的较大者更新当前值。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if len(nums) <= 1:
return False
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
dp = [0 for _ in range(target+1)]
for i in range(1, target+1):
if nums[0] <= i:
dp[i] = nums[0]
for i in range(1, len(nums)):
for j in range(target, 0, -1):
if nums[i] <= j:
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
return dp[-1] == target