class Solution:
def canPartition(self, nums: List[int]) -> bool:
S = sum(nums)
if S & 1: return False
S >>= 1
dp = [False] * (S+1)
dp[0] = True
for i in range(len(nums)):
for j in range(S, nums[i]-1, -1):
dp[j] = dp[j] or dp[j-nums[i]]
if dp[S]:
return True
return False
最初的 01背包问题是求能取得的最大价值。
二维转移方程为:
优化空间后的一组转移方程为:
伪代码:
背包问题可衍生为:
来讨论这几个问题:
322. 零钱兑换
这是一个完全背包问题,每个硬币可以不限次数的拿。与 01 的转移方程不同在于第二层循环要顺序遍历(而不是逆序)
求最小个数,初始 dp[] 元素全为最大 float('inf')。而第 0 个物品可以把第 0 个背包装满,所用物品数量 为 0,所以有
dp[0] = 0
。dp[j] = min(dp[j], dp[j-coins[i]]+1)
不难理解:416. 分割等和子集
这个问题相当于:数组中每个元素的值是它们的重量,每个元素的价值是 1,背包能否装满
能否装满是一个 True or False 问题,转移方案不能再用 max,而是 or :
就是,如果不取当前元素就已经满了,或容量为 j-nums[i] 的背包满了(那么 j-nums[i] + nums[i] = j,当前就肯定满了)。
494. 目标和
这个问题相当于:背包容量为 W,恰好装满的方案总数。
dp[i][j] 表示前 i 个元素把容量为 j 的背包装满的方案数。初始时只有 0 个元素能把容量为 0 的背包装满,所以
dp[0] = 1
。一组转移方程为:解释: