完全背包
思路:
- **初始化:**
- 当容量为0时,无论什么物品都放不下,所以 `dp[i][0]` 都是0。
- 只考虑物品0时,如果背包容量足够,就一直放物品0,直到背包装不下。
- **递推公式:**
- 对于 `dp[i][j]`,我们有两种选择:
- **不放物品 `i`:** 那么最大价值就是 `dp[i-1][j]`,也就是只考虑前 `i-1` 种物品,背包子容量为 `j` 的最大价值。
- **放物品 `i`:** 那么我们需要先给物品 `i` 腾出背包空间,剩余容量是 `j - weight[i]`。此时,我们继续考虑前 `i` 种物品,背包容量为 `j - weight[i]` 的最大价值,再加上物品 `i` 的价值 `value[i]`。
- 取两种情况的最大值:`dp[i][j] = max(dp[i-1][j], dp[i][j - weight[i]] + value[i])`
def complete_backpack_n_plus_1(n, bagWeight, weight, value):
"""
完全背包问题,物品数量为 n,求最大价值
Args:
n: 物品数量
bagWeight: 背包最大容量
weight: 每种物品的重量列表
value: 每种物品的价值列表
Returns:
最大价值
"""
# 1. 创建表格 (n 行)
dp = [[0 for _ in range(bagWeight + 1)] for _ in range(n)]
# 2. 初始化
for j in range(weight[0], bagWeight + 1):
dp[0][j] = dp[0][j - weight[0]] + value[0]
# 3. 填写表格
for i in range(1, n):
for j in range(bagWeight + 1):
if j < weight[i]:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
# 4. 返回答案
return dp[n - 1][bagWeight]
n, bagWeight = map(int, input().split())
weight = []
value = []
for _ in range(n):
w, v = map(int, input().split())
weight.append(w)
value.append(v)
result = complete_backpack_n_plus_1(n, bagWeight, weight, value)
print(result)
518. 零钱兑换 II
思路:
硬币无限,完全背包问题(遍历背包容量时候不需要逆序)。dp[j]表示能凑出金额j的方法数。
注意:凑金额0时有一种方法,什么硬币都不放。
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
"""
计算凑出总金额的硬币组合数。
Args:
amount: 总金额。
coins: 硬币面值列表。
Returns:
凑出总金额的组合数。
"""
dp = [0 ] * (amount + 1 ) # dp[j]表示凑出金额j的方法数
dp[0] = 1 # 金额为0 不用凑即可
# 遍历硬币
for coin in coins:
# 顺序遍历容量(完全背包,重复取没关系,不需要逆序)
for j in range(coin, amount+1):
dp[j] += dp[j - coin]
return dp[amount]
377. 组合总和 Ⅳ
思路:
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
'''求dp[i][j]目标为target,前i个元素的组合数量(实际上是排列,排列要先迭代容量)
dp[i][j] = dp[i-1][j] + dp[i][j-nums[i-1]]
=> dp[j] = dp[j] + dp[j-nums[i-1]]
'''
n = len(nums)
dp = [0] * (target+1)
# 初始化 target=0时候只有空集算 注意!
dp[0] = 1
# 先容量再元素(排列问题,顺序不同是新的组合,先遍历容量,再内容)
for j in range(1, target+1):
for num in nums:
if j >= num:
dp[j] += dp[j - num]
return dp[target]
57. 爬楼梯
思路:
dp[i]表示爬到有i个台阶的楼梯,有dp[i]种方法
对于j<=i,dp[i]+=dp[i-j] j∈[1,m]'
物品是每次可以爬的步数1-m,容量是楼梯的台阶数。物品可以无限取,完全背包问题,背包容量顺序遍历;
由于21 12爬的方法不同,是排列问题,先迭代容量,再迭代物品。
n,m = list(map(int, input().split()))
def climbing_stais(n, m):
dp = [0] * (n+1)
dp[0] = 1 # 方法的话0=1;硬币数量=1
for i in range(1, n+1):
for j in range(1,m+1):
if i >= j:
dp[i] += dp[i-j]
return dp[n]
print(climbing_stais(n,m))