题目
见参考1.
原题
给一个正整数列表, 问能否分割为两个集合, 使之加和相等.
抽象
选出若干个元素, 使其和为总和的一半.
每个元素可选可不选, 0-1 背包.
ac代码 py
两种解法.
二维dp 与 一维dp
from typing import List
class Solution:
def canPartition(self, nums: List[int]) -> bool:
return self.fun2(nums)
def fun1(self, nums: List[int]) -> bool:
return self.canPartition2(nums)
if sum(nums) % 2 == 1:
return False
half_sum = int(sum(nums) / 2)
# dp[i][j] 表示, 从 index \in [0,i] 的元素中挑若干个, 能否使和为 j
dp = [[False for j in range(half_sum + 1)] for i in range(len(nums))]
# 两种皆可
dp = [[False] * (half_sum + 1) for i in range(len(nums))]
# 初始化边界条件
for i in range(len(nums)):
dp[i][0] = True
if nums[0] <= half_sum:
dp[0][nums[0]] = True
for i in range(1, len(nums)):
# for j in range(1, half_sum + 1):
# 逆序, 正序 皆可
for j in range(half_sum, 0, -1):
# 价值太大, 一定不能放
if nums[i] > j:
dp[i][j] = dp[i - 1][j]
else:
# 可放可不放
dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
# 若已经求解, 直接返回
if dp[i][half_sum]:
return True
return dp[len(nums) - 1][half_sum]
def fun2(self, nums: List[int]) -> bool:
if sum(nums) % 2 == 1:
return False
half_sum = int(sum(nums) / 2)
# dp[i][j] 表示, 从 index \in [0,i] 的元素中挑若干个, 能否使和为 j
dp = [False] * (half_sum + 1)
# 初始化边界条件
dp[0] = True
for i in range(1, len(nums)):
# 只能逆序
for j in range(half_sum, 0, -1):
# 价值太大, 一定不能放
if nums[i] > j or j - nums[i] > half_sum:
break
else:
# 可放可不放
# dp[j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
dp[j] = dp[j] or dp[j - nums[i]]
# 若已经求解, 直接返回
if dp[half_sum]:
return True
return dp[half_sum]
if __name__ == '__main__':
x = Solution().canPartition([1, 5, 10, 6])
print(x)
自命题
对原题作更直观的改造.
"""
给定一个int数组, 判断是否可以从中挑若干个元素, 令其和为指定值。
要求: 动态规划求解.
case: judge([1,5,11,5],16) -> True; judge([1,5,11,5],13)->False
语言: py3
在线运行: https://c.runoob.com/compile/9
"""
from typing import List
def judge(arr: List[int], goal:int) -> bool:
# to be completed here
if __name__ == "__main__":
#True
#False
print(judge([1,5,11,5],16))
print(judge([1,5,11,5],13))