完全背包:
-
与01背包问题唯一不同的地方就是,
-
理论方面:每件物品都有无限个(也就是可以放入背包多次)
-
代码方面:在遍历顺序上。正是因为可以多次放入背包,所以要正序遍历。
-
-
测试代码:代码随想录
518. 零钱兑换 II
-
一看到钱币数量不限,就知道这是一个完全背包
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
#step1: 确定dp数组以及下标的含义: dp[j]:凑成总金额j的货币组合数为dp[j]
#step2: 确定递推公式: dp[j] += dp[j - coins[i]] (参考#494 目标和一样)
#step3: dp数组初始化
dp = [0]*(amount+1)
dp[0] = 1
#step4: 确定遍历顺序:
'''
还是遵循先物品后背包(虽说对于纯完全背包问题先遍历谁都可以,但是首先 先物品后背包 是为了与前面的遍历方式统一,
其次对于这道题而言只能是先物品后背包,因为求的是组合数不是排列数,对于顺序是有要求的。具体参考代码随想录)
'''
for i in range(len(coins)):
for j in range(coins[i], amount+1):
dp[j] = dp[j] + dp[j-coins[i]]
#step5: 打印检查
return dp[amount]
注意:
如果求组合数就是外层for遍历物品,内层for遍历背包。(本题)
- 如果求排列数就是外层for遍历背包,内层for遍历物品。
377. 组合总和 Ⅳ
-
从例题中可以看出求的是排列
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
#step1: 确定dp数组以及下标的含义: dp[i]: 凑成目标正整数为i的排列个数为dp[i]
#step2: 确定递推公式: dp[j] += dp[j - nums[i]] (参考#494 目标和一样)
'''但当进行到step4时会发现需要先遍历背包后遍历物品,因此此时的i和j需要调换位置,即dp[i] += dp[i - nums[j]]'''
#step3: dp数组如何初始化
dp = [0]*(target+1)
dp[0] = 1
#step4: 确定遍历顺序
'''
在#518. 零钱兑换 II中讨论过为什么需要先物品后背包,那么这道题是求排列则需要先背包后物品
'''
for i in range(1, target+1):
for j in range(len(nums)):
if i >= nums[j]:
dp[i] += dp[i - nums[j]]
#step5: 打印检查
return dp[target]
注意:
- 本题与动态规划:518.零钱兑换II (opens new window)就是一个鲜明的对比,一个是求排列,一个是求组合,遍历顺序完全不同。
- 求装满背包有几种方法,递归公式都是一样的,没有什么差别,但关键在于遍历顺序!