1049. 最后一块石头的重量 II
- 确定dp数组(dp table)以及下标的含义
dp[j] 在容量为j的背包里放入物体能够达到的最大重量- 确定递推公式
dp[j] = max(dp[j], dp[j - weights[i]] + values[i])- dp数组如何初始化
dp[0] = 0
for j in range(1, n + 1):
if j >= weights[0]: dp[j] = values[0]- 确定遍历顺序
先遍历物体再遍历背包- 举例推导dp数组
stones = [2,7,4,1,8,1]
capacity = int(round((2 + 7 + 4 + 1 + 8 + 1) / 2, 0)) = 12 (四舍五入)
i = 0时: [0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
i = 1时: [0, 0, 2, 2, 2, 2, 2, 7, 7, 9, 9, 9, 9]
i = 2时: [0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9, 11, 11]
i = 3时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
i = 4时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
i = 5时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
n = int(round(sum(stones) / 2, 0))
dp = [0] * (n + 1)
for j in range(1, n + 1):
if j >= stones[0]:
dp[j] = stones[0]
for i in range(1, len(stones)):
# 注意为了让 j >= stones[i],最后一个遍历的元素应该是 stones[i]
for j in range(n, stones[i] - 1, -1):
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])
return abs(sum(stones) - 2 * dp[-1])
494. 目标和
分析一
本题要如何转化为背包问题(使表达式结果为target)
既然为target,那么就一定有 left组合 - right组合 = target。
left + right = sum,而sum是固定的。right = sum - left
公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。
target是固定的,sum是固定的,left就可以求出来。
此时问题就是在集合nums中找出和为left的组合。
dp 五部曲
- 确定dp数组(dp table)以及下标的含义
这题是排列组合问题,所以dp定义为 填满容量为j的背包时有j种方法。- 确定递推公式(不太懂这个递推公式)
dp[j] += dp[j - nums[i]]; i = 0,1,…,n- dp数组如何初始化
如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。
其他位置初始化为0,由dp[0]推导而来- 确定遍历顺序
先遍历物体再遍历背包容量- 举例推导dp数组(不太懂怎么推)
nums = [1,1,1,1,1], target = 3
n = (target + sum) / 2 = (3 + 5) / 2 = 4
0 1 2 3 4
nums[0] 1 1 0 0 0
nums[1] 1 2 1 0 0
nums[2] 1 3 3 1 0
nums[3] 1 4 6 4 1
nums[4] 1 5 10 10 5
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
s = sum(nums)
# 注意要用绝对值,要同时考虑全正号和全负号的情况
if (target + s) % 2 != 0 or (abs(target) > s):
return 0
n = (target + sum(nums)) // 2
dp = [1] + [0] * n
for i in range(len(nums)):
for j in range(n, nums[i] - 1, -1):
dp[j] += dp[j - nums[i]]
return dp[-1]
474.一和零
题目链接:474.一和零
分析一
问题转化:
本题中strs 数组里的元素就是物品,每个物品都是一个!
而m 和 n相当于是一个背包,两个维度的背包。
其实就是01背包循环数组的升维版。
DP五部曲
- 确定dp数组(dp table)以及下标的含义
dp[i][j] 表示用最多有 i 个 0 和 j 个 1 的子集最多有多少个(最大子集大小)- 确定递推公式
dp[i][j] = dp[i - zeroNum][j - oneNum] + 1 (没看懂这个)
注:zeroNum 和 oneNum 表示前一个strs 0 的个数和 1 的个数- dp数组如何初始化
dp[0][0] = 0- 确定遍历顺序:先遍历物品后遍历背包容量(之前循环数组说过原因)
- 举例推导dp数组
strs = [“10”,“0001”,“111001”,“1”,“0”]
m = 5, n = 3
(1) 遍历物品1
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
(2) 遍历物品2
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 2, 2]
[0, 1, 2, 2]
(3) 遍历物品3
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 2, 2]
[0, 1, 2, 2]
(4) 遍历物品4
[0, 1, 1, 1]
[0, 1, 2, 2]
[0, 1, 2, 2]
[0, 1, 2, 2]
[0, 1, 2, 3]
[0, 1, 2, 3]
(5) 遍历物品5
[0, 1, 1, 1]
[1, 2, 2, 2]
[1, 2, 3, 3]
[1, 2, 3, 3]
[1, 2, 3, 3]
[1, 2, 3, 4]
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
# 初始化
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 遍历字符
for string in strs:
zero_num, one_num = 0, 0
for c in string:
if c == "0":
zero_num += 1
else:
one_num += 1
for i in range(m, zero_num - 1, -1):
for j in range(n, one_num - 1, -1):
dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)
return dp[-1][-1]