一维数组动态规划
题目要求将数组分为两个相等子集的和,那么如果为真,那么这个数组的总和必定是偶数(条件一),且该数组的最大值必不大于该数组和的一半(条件二)。
那么该问题即可转化为求解和为是否有和为total = sum(nums) // 2的子数组问题。
使用一维数组dp = [False] * (total+1),
边界条件:当和为0时,必为真,则dp[0] = True
例:求解和为i (i = (1,2,…total)),
首先遍历每个num in nums,且,为防止重复,i必须逆序遍历。状态转移方程为:当i >= num: dp[i] = dp[i] | dp[i-num]
状态转移方程的意思是:是否有和为i的子数组取决于之前i的状态与i-num的状态。
为何要逆序:
例:[1,2,3,6],dp = [True, False, False, False, False,False, False]
那么遍历完num=1时,若i不逆序,则当i=1时,dp[i] = True(因为dp[i-num] = True),而当i=2时,dp[i] = True,因为dp[i-num] = True(此时i= 2, 因此i-num=1, 而刚更新过的dp[1] = True)。因此i必须逆序,则,较小的和就不会影响较大的和。
为何状态转移方程dp[i] = dp[i] | dp[i-num],
当遍历num in nums时,有可能会出现dp[i-num] = False的情况,然而当前的状态不应该影响以前的状态,若之前遍历的时候dp[i] = True,则若不加 |dp[i],则会出错。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 == 1: # 条件一
return False
n = sum(nums) // 2
if max(nums) > n: # 条件二
return False
dp = [False] * (n+1)
dp[0] = True # 初始值
for num in nums:
for i in range(n, 0, -1):
if i >= num:
dp[i] = dp[i-num] | dp[i] # 状态转移方程
return dp[n]