题目链接:
题目描述:
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例 1:
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
示例 2:
输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。
示例 3:
输入:amount = 10, coins = [10]
输出:1
提示:
1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins 中的所有值 互不相同
0 <= amount <= 5000
解析:
题目要求从coins数组中选出一些元素放入背包中,且这些元素满足一定的条件(凑成amount),这种
思路满足背包问题。由于每种硬币数量无限,所以是多重背包问题。
与多重背包的对应关系:coins[i]相当于物品,amount相相当于背包容量,价值相当于组合数量
递归四部曲:
确定dp数组:dp[j]表示凑成j的硬币组合数量
确定递推公式:dp[j] = dp[j] + dp[j - coins[i]],因为求得是组合的数量,所以dp值是累加,而不是取最大值,因为取得是所有情况而不是特定的某种情况
初始化dp数组:dp[0] = 1意义是凑成0的情况只有一种,即什么都不取
确定遍历顺序:求组合的数量的话就先遍历物品再遍历容量,均采用顺序遍历
代码如下:
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i], amount + 1):
dp[j] += dp[j - coins[i]]
return dp[amount]
这里说一下求组合数量得先遍历物品再遍历容量的原因:
假设dp[0]=1,dp[1]=3。那么先遍历物品再遍历容量的话就只能先到取1,再取3,只能取到{1, 3}这个组合,而不能取到{3, 1}这个组合。即dp[4]中对于1和3就只有{1, 3}这种情况
再说一下求排列数量得先遍历容量再遍历容物品的原因:
假设dp[0]=1,dp[1]=3。那么先遍历容量再遍历物品的话每个容量都要经过1和3的计算,即会先对取1的情况进行决策(可以取到{1, 3}),再对取3的情况进行决策(可以取到{3, 1})。即dp[4]中对于1和3有{1, 3}和{3, 1}2种情况.