题目描述
题目链接:518. 零钱兑换 II
给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例1:
1 2 3 4 5 6 7
| 输入: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:
1 2 3
| 输入:amount = 3, coins = [2] 输出:0 解释:只用面额 2 的硬币不能凑成总金额 3 。
|
示例2:
1 2
| 输入:amount = 10, coins = [10] 输出:1
|
提示:
1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins
中的所有值 互不相同
0 <= amount <= 5000
我的题解
方法一:01背包思想
思路
设dp[i][j]为在前i个银币中凑成金额为j的种类数量,考虑第i个硬币参不参与凑数过程:
- 不参与,则dp[i][j] = dp[i-1][j]
- 参与,考虑第i个硬币选k次,最多选 j / coins[i]次,因此求dp[ i - 1][j - coins[i] * k]之和
考虑动态规划初始化,因为当硬币值为n时,可以凑成金额为k*n的种类有一种,因此为了保证dp[ i - 1][j - coins[i] * k]中j - coins[i] * k == 0时有一种选择,因此我们把dp[i][0]初始化为1.
以示例一举例,求得dp数组为
1 2 3 4
| 1 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 3 3 1 1 2 2 3 4
|
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public int change(int amount, int[] coins) { int[][] dp = new int[coins.length + 1][amount + 1]; for (int i = 0; i < dp.length; i++) { dp[i][0] = 1; } for (int i = 1; i < dp.length; i++) { for (int j = 1; j < dp[0].length; j++) { for (int k = 0; k <= j / coins[i - 1]; k++) { dp[i][j] += dp[i - 1][j - k * coins[i - 1]]; } } } return dp[coins.length][amount]; } }
|
方法二:完全背包
思路
和上述01背包思想不同,完全背包直接利用了dp[i][j]语义,方法一解法是通过遍历来获取含第i个硬币的组合,因此多了一层循环,而完全背包递推公式为:
1
| dp[i][j] += dp[i - 1][j] + dp[i][j - coins[i - 1]];
|
注意后半部分,dp[i][j - coins[i - 1]],也就是直接添加第i个硬币已经参与并且值为j - coins[i-1]的种类
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { public int change(int amount, int[] coins) { int[][] dp = new int[coins.length + 1][amount + 1]; for (int i = 0; i < dp.length; i++) { dp[i][0] = 1; } for (int i = 1; i < dp.length; i++) { for (int j = 1; j < dp[0].length; j++) { if (j >= coins[i - 1]) { dp[i][j] += dp[i - 1][j] + dp[i][j - coins[i - 1]]; } else { dp[i][j] += dp[i - 1][j]; } } } return dp[coins.length][amount]; } }
|
使用一维数组优化:
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int change(int amount, int[] coins) { int[] dp = new int[amount + 1]; dp[0] = 1; for (int i = 0; i < coins.length; i++) { for (int j = coins[i]; j <= amount; j++) { dp[j] += dp[j - coins[i]]; } } return dp[amount]; } }
|