LeetCode 322. Coin Change
2020-06-07 09:37:59 # leetcode # core problems

Problem

LeetCode 322. Coin Change

1. 题目简述

给出一些面值的硬币coins,和目标金额mount,找出最少需要多少枚硬币能够组成amount,如果无法组成,则返回-1。例如:

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

2. 算法思路

相关问题:

  1. LeetCode 39. Combination Sum
  2. LeetCode 40. Combination Sum II
  3. LeetCode 216. Combination Sum III

经典老题目,我们需要留意的是它和combination sum的区别,combination sum是找具体的组合,而coin change只要找最小的合法组合。因此,这道题我们考虑用dp,而不是回溯法。

那么如何找到最优子问题呢?对于例子中的amount为11,它有几种组合方式:(1)10 + 1 (2)9 + 2 (3)6 + 5,找出这三者中最小的即可。其base case是当amount为0的时候,组合方式有且只有一种。递推式如下:

$$dp[i] = Math.min(dp[i - coin] + 1, dp[i]) (for coin in coins)$$

动态规划(top-down)

初始化的值没必要设置成Integer.MAX_VALUE,最大设置为amount+1即可,因为最多也就是amount个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
34
35
36
37
class Solution {
// 注意和combination sum的区别,combination sum是要找所有的解决方案,所以采用回溯,这里只要找最少的那个就好了。
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];

return getMinCoins(coins, amount, dp, amount + 1);
}

private int getMinCoins(int[] coins, int amount, int[] dp, int max) {
if (amount < 0) {
return -1;
} else if (amount == 0) {
return 0;
}

if (dp[amount] != 0) {
return dp[amount];
}

// 这里不用min,直接用dp[amount]直接操作也可以的!!!
// dp[amount] = max + 1;
// for (int coin : coins) {
// int temp = getMinCoins(coins, amount - coin, dp, max);
// dp[amount] = temp == -1 ? dp[amount] : Math.min(dp[amount], temp + 1);
// }
//dp[amount] = dp[amount] == max + 1 ? -1 : dp[amount];

int min = max + 1;
for (int coin : coins) {
int temp = getMinCoins(coins, amount - coin, dp, max);
min = temp == -1 ? min : Math.min(min, temp + 1);
}

dp[amount] = min == max + 1 ? -1 : min;
return dp[amount];
}
}

动态规划(Bottom-up)

这里我们需要注意的只有边界case,也就是dp[0] = 0,这里要和coin change 2进行区分,那个是求方式,这个是求硬币数,不一样的!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;

for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (i - coin >= 0) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}

return dp[amount] >= max ? -1 : dp[amount];
}
}