518. 零钱兑换 II (dp)
难度中等
给你一个整数数组 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
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(amount+1): if j-coins[i]>=0: dp[j]+=dp[j-coins[i]] return dp[amount]
dp[i][j]
的定义如下:
若只使用前 i
个物品(可以重复使用),当背包容量为 j
时,有 dp[i][j]
种方法可以装满背包。
换句话说,翻译回我们题目的意思就是:
若只使用 coins
中的前 i
个硬币的面值,若想凑出金额 j
,有 dp[i][j]
种凑法。
经过以上的定义,可以得到:
base case 为 dp[0][..] = 0, dp[..][0] = 1
。因为如果不使用任何硬币面值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。
class Solution { public: int change(int amount, vector<int>& coins) { int n = coins.size(); vector<vector<int>> dp(n+1,vector<int>(amount+1,0)); dp[0][0] = 1; for(int i = 1;i <=n;++i) { for(int j = 0;j <=amount;j++) { if (j-coins[i-1]>=0) { dp[i][j] = dp[i][j-coins[i-1]] + dp[i-1][j]; } else { dp[i][j] = dp[i-1][j]; } } } return dp[n][amount]; } };