518.零钱兑换 II
题目
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 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
方法一(暴力递归)
例如:arr = [200, 100, 50, 20, 5, 1],aim = 1000
对于第一个元素200:
选择0张200的,求出用后面的面值凑出1000的方法数
选择1张200的,求出用后面的面值凑出800的方法数
选择2张200的,求出用后面的面值凑出600的方法数
………
选择5张200的,求出用后面的面值凑出0的方法数
将以上方法数加起来,就是答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static int coins1(int[] arr, int aim) { if (arr == null || arr.length == 0 || aim < 0) { return 0; } return process1(arr, 0, aim); }
public static int process1(int[] arr, int index, int aim) { int res = 0; if (index == arr.length) { res = aim == 0 ? 1 : 0; } else { for (int i = 0; arr[index] * i <= aim; i++) res += process1(arr, index + 1, aim - arr[index] * i); } return res; }
|
方法二(动态规划)
由暴力递归改动态规划有如下几步:
分析可变参数,本题中返回值仅由aim和index两个参数决定。因此可变参数为2个。需要一个二维dp数组。
分析可变参数的变化范围:index的变化范围为数组arr的长度,aim的变化范围为0到aim这aim+1个值。分析完这前两步,我们就可以构造dp数组
1 2
| int[][] dp = new int[arr.length][aim + 1];
|
确定Base Case: i为0和j为0时的情况
根据暴力递归的递归主体,分析出一个普遍位置是怎么依赖的。然后依次填充dp数组
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public int change(int aim, int[] arr) { if(aim == 0) return 1; if (arr == null || arr.length == 0 || aim < 0) { return 0; } int[][] dp = new int[arr.length][aim + 1]; for (int i = 0; i < arr.length; i++) { dp[i][0] = 1; } for (int j = 1; arr[0] * j <= aim; j++) { dp[0][arr[0] * j] = 1; } int num = 0; for (int i = 1; i < arr.length; i++) { for (int j = 1; j <= aim; j++) { num = 0; for (int k = 0; j - arr[i] * k >= 0; k++) { num += dp[i - 1][j - arr[i] * k]; } dp[i][j] = num; } } return dp[arr.length - 1][aim]; }
|