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
递归 -> 记忆化搜索 -> 动态规划 -> 动态规划的优化
public class Coinsway {
public static void main(String[] args) {
int[] arr = {5, 10, 50, 100};
int aim = 1000;
System.out.println(ways(arr, aim));
System.out.println(waysMemory(arr, aim));
System.out.println(waysDp(arr, aim));
System.out.println(waysDpGood(arr, aim));
}
public static int ways(int[] arr, int aim){
if(arr == null || arr.length == 0 || aim < 0){
return 0;
}
return process(arr, 0, aim);
}
/**
*
* @param arr 银币种类
* @param index 当前到第几个了
* @param rest 剩余金额
* @return 表示有多少种方式
* 可以自由使用arr[index...]所有的面值,每一种面值都可以使用任意张,
* 组成rest,有多少种方法.
*/
private static int process(int[] arr, int index, int rest) {
/* 在for循环位置已经判断过了
if(rest < 0){
return 0;
}*/
if(index == arr.length){//没有货币可以选择了
return rest == 0 ? 1 : 0;
}
int res = 0;
for(int k = 0; k * arr[index] <= rest; k++){
res += process(arr, index + 1, rest - k * arr[index]);
}
return res;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
public static int waysMemory(int[] arr, int aim){
if(arr == null || arr.length == 0 || aim < 0){
return 0;
}
int N = arr.length;
// HashMap<String, Integer> map = new HashMap<>();
int[][] dp = new int[N + 1][aim + 1];
for (int i = 0; i <= N; i++){
for (int j = 0; j <= aim; j++) {
dp[i][j] = -1;
}
}
return processMemory(arr, 0, aim, dp);
}
private static int processMemory(int[] arr, int index, int rest, int[][] dp) {
if(dp[index][rest] != -1){
return dp[index][rest];
}
if(index == arr.length){//没有货币可以选择了
dp[index][rest] = rest == 0 ? 1 : 0;
return dp[index][rest];
}
int res = 0;
for(int k = 0; k * arr[index] <= rest; k++){
res += processMemory(arr, index + 1, rest - k * arr[index], dp);
}
dp[index][rest] = res;
return dp[index][rest];
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
public static int waysDp(int[] arr, int aim){
if(arr == null || arr.length == 0 || aim < 0){
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 1;
for(int index = N-1; index >= 0; index--){
for(int rest = 0; rest <= aim; rest++){
// dp[index][rest] = ?
int ways = 0;
for(int k = 0; k * arr[index] <= rest; k++){
ways += dp[index+1][rest-(arr[index] * k)];
}
dp[index][rest] = ways ;
}
}
return dp[0][aim];
}
public static int waysDpGood(int[] arr, int aim){
if(arr == null || arr.length == 0 || aim < 0){
return 0;
}
int N = arr.length;
int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 1;
for(int index = N-1; index >= 0; index--){
for(int rest = 0; rest <= aim; rest++){
dp[index][rest] = dp[index+1][rest];
if(rest - arr[index] >= 0){
dp[index][rest] += dp[index][rest - arr[index]];
}
}
}
return dp[0][aim];
}
}