问题描述
代码展示
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if len(nums) <= 1:
return False
total_sum = sum(nums)
if total_sum % 2 != 0: # 总和为奇数,无法分成两个相等的子集
return False
target_sum = total_sum // 2
dp = [[False] * (target_sum + 1) for _ in range(len(nums) + 1)]
# 初始化第一列
for i in range(len(nums) + 1):
dp[i][0] = True
for i in range(1, len(nums) + 1):
for j in range(1, target_sum + 1):
dp[i][j] = dp[i-1][j]
if j >= nums[i-1]:
dp[i][j] = dp[i][j] or dp[i-1][j-nums[i-1]]
return dp[len(nums)][target_sum]
这段代码实现了一个判断给定数组是否可以被划分成两个和相等的子集的功能。
首先,如果数组长度小于等于1,则无法划分,直接返回False。
然后,计算数组中所有元素的总和,如果总和为奇数,则无法划分成两个相等的子集,直接返回False。
接下来,计算目标和,即总和的一半。创建一个二维的布尔型动态规划数组dp
,其中dp[i][j]
表示前i
个元素是否可以组成和为j
的子集。
然后,初始化动态规划数组的第一列,将其设为True。这是因为当目标和为0时,任何元素都可以不选,所以前i
个元素都可以组成和为0的子集。
接着,遍历数组元素,对于每个元素nums[i-1]
,在目标和范围内(从1到target_sum),更新动态规划数组。对于每个位置(i, j)
,如果当前元素不选,则该位置的值与上一行相同,即dp[i][j] = dp[i-1][j]
;如果当前元素选择了,并且剩余和等于当前位置的值减去当前元素的值,则该位置的值为True,即dp[i][j] = dp[i-1][j-nums[i-1]]
。
最后,返回动态规划数组的最后一个位置的值,即dp[len(nums)][target_sum]
,表示前len(nums)
个元素是否可以组成和为目标和的子集。
背包问题和动态规划问题
这段代码考察的知识点是动态规划(Dynamic Programming)和背包问题(Knapsack Problem)。
动态规划是一种解决问题的方法,它通过将问题分成更小的子问题,并保存子问题的解,从而避免重复计算,提高效率。这段代码中使用了动态规划来解决背包问题。
背包问题是一个经典的组合优化问题,通常有两种变体:0-1背包问题和完全背包问题。这段代码涉及的是0-1背包问题,即每个物品只能选择取或不取。
在这段代码中,给定一个整数数组nums,我们需要判断能否将其划分为两个和相等的子集。首先,我们计算数组的总和total_sum。如果总和为奇数,那么无法将其划分为两个相等的子集,直接返回False。
然后,我们将目标和target_sum设置为总和的一半。创建一个二维的动态规划表dp,dp[i][j]表示前i个物品中是否存在一种方式使得和为j。初始化第一列为True,因为当j为0时,任何物品都可以组成和为0的子集。
接下来,使用两层循环遍历数组nums和目标和target_sum。对于每个元素nums[i-1],我们有两种选择:取它或不取它。如果当前和j大于等于nums[i-1],那么可以选择取这个元素,使得dp[i][j]为True;否则,不取这个元素,保持dp[i][j]不变。最终,返回dp[len(nums)][target_sum]的结果,即是否存在一种方式使得前len(nums)个物品的和为target_sum。
这段代码使用动态规划的思想解决了0-1背包问题,判断了数组能否被划分为两个和相等的子集。