From 5ab0a94c7152431b23303a6e64dc18a07c9ed37f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 05:36:45 -0500 Subject: [PATCH] =?UTF-8?q?Update=200416.=E5=88=86=E5=89=B2=E7=AD=89?= =?UTF-8?q?=E5=92=8C=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0416.分割等和子集.md | 107 +++++++++++++++++----------- 1 file changed, 65 insertions(+), 42 deletions(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 8115e18e..bb210e29 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -296,62 +296,85 @@ false true false false false true true false false false true true ``` ### Python: +卡哥版 ```python -# 一维度数组解法 class Solution: def canPartition(self, nums: List[int]) -> bool: - target = sum(nums) - if target % 2 == 1: return False - target //= 2 - dp = [0] * (target + 1) - for i in range(len(nums)): - for j in range(target, nums[i] - 1, -1): - dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) - return target == dp[target] + _sum = 0 + + # dp[i]中的i表示背包内总和 + # 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200 + # 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了 + dp = [0] * 10001 + for num in nums: + _sum += num + # 也可以使用内置函数一步求和 + # _sum = sum(nums) + if _sum % 2 == 1: + return False + target = _sum // 2 + + # 开始 0-1背包 + for num in nums: + for j in range(target, num - 1, -1): # 每一个元素一定是不可重复放入,所以从大到小遍历 + dp[j] = max(dp[j], dp[j - num] + num) + + # 集合中的元素正好可以凑成总和target + if dp[target] == target: + return True + return False + ``` - +二维DP版 ```python -# 二维度数组解法 class Solution: def canPartition(self, nums: List[int]) -> bool: - target = sum(nums) - nums = sorted(nums) + + total_sum = sum(nums) - # 做最初的判断 - if target % 2 != 0: + if total_sum % 2 != 0: return False - # 找到 target value 可以认为这个是背包的体积 - target = target // 2 + target_sum = total_sum // 2 + dp = [[False] * (target_sum + 1) for _ in range(len(nums) + 1)] - row = len(nums) - col = target + 1 + # 初始化第一行(空子集可以得到和为0) + for i in range(len(nums) + 1): + dp[i][0] = True - # 定义 dp table - dp = [[0 for _ in range(col)] for _ in range(row)] - - # 初始 dp value - for i in range(row): - dp[i][0] = 0 - - for j in range(1, target): - if nums[0] <= j: - dp[0][j] = nums[0] - - # 遍历 先遍历物品再遍历背包 - for i in range(1, row): - - cur_weight = nums[i] - cur_value = nums[i] - - for j in range(1, col): - if cur_weight > j: + for i in range(1, len(nums) + 1): + for j in range(1, target_sum + 1): + if j < nums[i - 1]: + # 当前数字大于目标和时,无法使用该数字 dp[i][j] = dp[i - 1][j] else: - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight] + cur_value) - - # 输出结果 - return dp[-1][col - 1] == target + # 当前数字小于等于目标和时,可以选择使用或不使用该数字 + dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] + + return dp[len(nums)][target_sum] + +``` +一维DP版 +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + + total_sum = sum(nums) + + if total_sum % 2 != 0: + return False + + target_sum = total_sum // 2 + dp = [False] * (target_sum + 1) + dp[0] = True + + for num in nums: + # 从target_sum逆序迭代到num,步长为-1 + for i in range(target_sum, num - 1, -1): + dp[i] = dp[i] or dp[i - num] + + return dp[target_sum] + ``` ### Go: