322.零钱兑换
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
1 2 3
| 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1
|
示例 2:
1 2
| 输入:coins = [2], amount = 3 输出:-1
|
示例 3:
1 2
| 输入:coins = [1], amount = 0 输出:0
|
完全背包
在01背包中,物品只能被选择1次,而在完全背包中,物品能被选无数次
很自然的写出回溯表达式:
回溯代码:
这里的if (i < 0) return c == 0 ? 0 : Integer.MAX_VALUE / 2;
这个表达式很关键,表明了如果无法找零,那么直接标记缓存数组为最大值,从而在接下来的最小值比较中总是不被选择
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 30 31
| import java.util.Arrays;
class Solution { private int[] coins; private int[][] memo;
public int coinChange(int[] coins, int amount) { this.coins = coins; int n = coins.length; memo = new int[n][amount + 1]; for (int[] row : memo) Arrays.fill(row, -1); int ans = dfs(n - 1, amount); return ans < Integer.MAX_VALUE / 2 ? ans : -1; }
private int dfs(int i, int c) { if (i < 0) return c == 0 ? 0 : Integer.MAX_VALUE / 2; if (memo[i][c] != -1) return memo[i][c]; if (c < coins[i]) return memo[i][c] = dfs(i - 1, c); return memo[i][c] = Math.min(dfs(i - 1, c), dfs(i, c - coins[i]) + 1); } }
|
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 30 31 32 33
| class Solution {
public int coinChange(int[] coins, int amount) { int n = coins.length; int[][] f = new int[n + 1][amount + 1]; for (int i = 0; i <= n; i++) { for (int j = 0; j <= amount; j++) { f[i][j] = Integer.MAX_VALUE / 2; } } f[0][0] = 0;
for (int i = 0; i < n; i++) { for (int c = 0; c <= amount; c++) { if (c < coins[i]) { f[i + 1][c] = f[i][c]; } else { f[i + 1][c] = Math.min(f[i][c], f[i + 1][c - coins[i]] + 1); } } }
return f[n][amount] == Integer.MAX_VALUE / 2 ? -1 : f[n][amount]; } }
|
这里不需要像01背包一样从后往前遍历,因为完全背包问题的状态转移是f(i+1)
到f(i+1)
的,并不完全依赖于f(i)
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
| import java.util.Arrays;
class Solution { public int coinChange(int[] coins, int amount) { int[] f = new int[amount + 1]; Arrays.fill(f, Integer.MAX_VALUE / 2); f[0] = 0;
for (int x : coins) { for (int c = x; c <= amount; ++c) { f[c] = Math.min(f[c], f[c - x] + 1); } }
int ans = f[amount]; return ans < Integer.MAX_VALUE / 2 ? ans : -1; } }
|