思路四:优化dp数组的动态规划,画图判断是否可以优化
代码
//1.暴力递归超时 public int change(int amount, int[] coins) { if (amount == 0) return 1; if (amount < 0) return 0; return recur(amount, coins, new HashMap<>()); } //记录已使用的key,用于去重处理 Set<String> used = new HashSet<>(); public int recur(int amount, int[] coins ,Map<Integer, Integer> map) { if (amount == 0) { StringBuilder key = new StringBuilder(); for (int coin : coins) { key.append(map.getOrDefault(coin, 0)); } if (!used.contains(key.toString())) { used.add(key.toString()); return 1; } return 0; } if (amount < 0) return 0; int count = 0; for (int coin : coins) { if (amount - coin >= 0) { map.put(coin, map.getOrDefault(coin, 0) + 1); count += recur(amount - coin, coins, map); map.put(coin, map.getOrDefault(coin, 0) - 1); } } return count; } //1.暴力递归超时 public int change(int amount, int[] coins) { if (amount == 0) return 1; if (amount < 0) return 0; return recur(amount, coins, 0); } public int recur(int amount, int[] coins, int index) { if (amount == 0) return 1; if (amount < 0 || index >= coins.length) return 0; int k = amount / coins[index], count = 0; for (int i = 0; i <= k; i++) { count += recur(amount - i * coins[index], coins, index + 1); } return count; } //2. 备忘录 Map<String, Integer> map = new HashMap<>(); public int change(int amount, int[] coins) { if (amount == 0) return 1; if (amount < 0) return 0; return recur(amount, coins, 0); } public int recur(int amount, int[] coins, int index) { if (amount == 0) return 1; if (amount < 0 || index >= coins.length) return 0; String key = coins[index] + "-" + amount; if (map.containsKey(key)) return map.get(key); int k = amount / coins[index], count = 0; for (int i = 0; i <= k; i++) { count += recur(amount - i * coins[index], coins, index + 1); } map.put(key, count); return count; } //3.动态规划 public int change(int amount, int[] coins) { //dp[i][j]表示使用小于等于i种硬币可以表示j金额的所有可能性 int[][] dp = new int[coins.length + 1][amount + 1]; // 有一种方案凑成 0 元,那就是 一个也不选 dp[0][0] = 1; for (int i = 1; i <= coins.length; i++) { for (int j = 0; j <= amount; j++) { //不管是否使用i种金额能凑出几种可能性,至少可能性是大于等于i-1种金额凑出的可能性 dp[i][j] = dp[i - 1][j]; if (j >= coins[i - 1]) dp[i][j] += dp[i][j - coins[i - 1]]; } } return dp[coins.length][amount]; } //4.优化dp数组的动态规划,由于使用了前一行的dp数组和前一个元素,所以是可以优化的,画图就很快发现 public int change(int amount, int[] coins) { if (amount == 0) return 1; if (amount < 0) return 0; int[] dp = new int[amount + 1]; dp[0] = 1; for (int coin : coins) { for (int i = 0; i <= amount; i++) { if (i - coin >= 0) dp[i] += dp[i - coin]; } } return dp[amount]; }