给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
分析:
硬币可以无限使用,为完全背包问题。可以用一个dp数组存储前面的值。有递推公式:
F
(
i
)
=
min
c
j
(
0
<
j
<
c
.
l
e
n
g
t
h
)
F
(
i
−
c
j
)
+
1
F(i) = \min_{c_j(0<j<c.length)}F(i - c_j) + 1
F(i)=cj(0<j<c.length)minF(i−cj)+1
F(i)代表当前amount等于i时所需最小硬币数。cj代表硬币额,将所有硬币额遍历一遍以求得最小值。
这里初始化数组一定要注意,dp[0] = 0,另外的初始化为amount+1,当最后结果大于amount则返回-1。
这样写的前提是假设coins amount都不会为负数,虽然这是常识,但是面试时可以先问清楚。
class Solution {
public int coinChange(int[] coins, int amount) {
if (coins == null || amount < 0) return -1;
int[] dp = new int[amount+1];
Arrays.fill(dp, amount+1);
dp[0] = 0;
for (int coin : coins) {
for (int i = 1; i <= amount; ++i) {
if (i - coin >= 0)
dp[i] = Math.min(dp[i - coin] + 1, dp[i]);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
经过检验,外层循环和内层循环条件交换后都可以通过.
class Solution {
public int coinChange(int[] coins, int amount) {
if (coins == null || amount < 0) return -1;
int[] dp = new int[amount+1];
Arrays.fill(dp, amount+1);
dp[0] = 0;
for (int i = 1; i <= amount; ++i) {
for (int coin : coins) {
if (i - coin >= 0)
dp[i] = Math.min(dp[i - coin] + 1, dp[i]);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}