1049. 最后一块石头的重量 II
减法运算,求和
def lastStoneWeightII(self, stones: List[int]) -> int:
# 小的被粉碎 大的变为y-x
# 尽量让石头分成重量相同的两堆
# 相当于寻找最接近sum的一半的值
s = sum(stones)
# 如果sum不为2的倍数 s//2会得到比较小的部分总和
weight = s//2
# 二维dp
dp = [[0] * (weight+1) for i in range(len(stones))]
for i in range(len(stones)):
for j in range(weight+1):
if j >= stones[i]:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i])
else:
dp[i][j] = dp[i-1][j]
# print(dp)
return s-2*dp[-1][-1]
# 一维dp
# dp = [0] * (weight+1)
# for i in range(len(stones)):
# for j in range(weight,stones[i]-1,-1):
# dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])
# print(dp)
return s-2*dp[-1]
494. 目标和
转化为01背包问题:
假设加法的总和为x,那么减法对应的总和就是sum - x。
所以我们要求的是 x - (sum - x) = target
x = (target + sum) / 2
求组合类问题的公式:
dp[j] += dp[j - nums[i]]
重点在问题到01背包的转换,如何设置物品size和背包容量
def findTargetSumWays(self, nums: List[int], target: int) -> int:
# 符合条件的个数
# 相当于放入nums的数据 背包容量为target
# 类比01背包 往背包里放物品 分析背包容量
# 是否要放物品的操作0/1 类比本题的+/-
s = sum(nums)
# dp = [[s] * (len(nums)) for _ in range(target+1)]
set_size = (s+target) // 2
if abs(target) > s or (s + target) % 2 == 1: return 0
dp = [0] * (set_size+1)
dp[0] = 1
for i in range(len(nums)):
# 01背包内层倒序
for j in range(set_size, nums[i]-1, -1):
# dp[j] 符合和为j条件的个数
dp[j] += dp[j-nums[i]]
print(dp)
return dp[-1]
474. 一和零
dp数组就表示当前i和j个0和1时 有多少种情况
把0和1的个数视为背包重量
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
# 最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1
# 先把strs转化为 包含0,1的个数的列表
dp = [[0] * (n+1) for _ in range(m+1)]
count = []
for s in strs:
zeros = s.count('1')
ones = s.count('0')
if zeros <= m and ones <= n:
dp[zeros][ones] = 1
else:
continue
# 其实相当于两个维度都是背包容量
# 两个一维滚动数组 从后遍历
for i in range(m,-1,-1):
for j in range(n,-1,-1):
if i >= zeros and j >= ones:
dp[i][j] = max(dp[i][j], dp[i-zeros][j-ones] + 1)
print(dp)
return dp[m][n]