322. 零钱兑换 + 动态规划 + 完全背包 + 硬币问题
题目来源
题目详情
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入: coins = [1, 2, 5]
, amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2]
, amount = 3
输出: -1
示例 3:
输入: coins = [1], amount = 0
输出: 0
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
题解分析
解法一:动态规划之完全背包变形
- 本题与背包问题极其类似,特别是看到【所有】等关键词,以及题目中提到的【假设每一种面额的硬币有无限个】更可以确信使用完全背包解决。
- 我们假设dp[i][j]表示使用前i种硬币凑成金额j需要的最小硬币数,那么什么情况可以推进状态的变化呢?
- 第一种情况是我们只选前i-1种硬币就能拼凑出j金额。
- 第二种情况是现在只能拼凑出j-coins[i-1]金额,但是也使用了i种硬币。这种情况比较难以理解,我们可以这样想,因为每种硬币的个数都是无限的,即使我们之前使用了第i种硬币使硬币金额总数达到了j-coins[i-1],我们还是可以重复使用第i种硬币来使结果金额达到j。
- 综上,我们可以得出动态转移方程:dp[i][j] = min(dp[i-1][j], dp[i][j - coins[i]] + 1);
- 需要注意的是,以上的状态转移方程只有在j>=coins[i]时才适用,当j<coins[i]时,dp[i][j] = dp[i-1][j],即后一种情况不用考虑。
- 此外,本题还有一个关键点就是边界值的初始化。这里需要把所有的dp[0][j]赋值为最大值,即表示不使用任何硬币一定无法拼凑出任何金额;把所有dp[i][0]赋值为0表示使用前i种硬币拼凑出金额0只有0种方案,即什么硬币都不使用。
class Solution {
public int coinChange(int[] coins, int amount) {
int n = coins.length;
// dp[i][j]表示使用前i种硬币凑成金额j需要的最小硬币数
int[][] dp = new int[n+1][amount+1];
// dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i]] + 1);
int maxs = 0x3f3f3f3f;
for(int j=0; j<=amount; j++){
dp[0][j] = maxs;
}
for(int i=0; i<=n; i++){
dp[i][0] = 0;
}
for(int i=1; i<=n; i++){
for(int j=0; j<=amount; j++){
if(j >= coins[i-1]){
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-coins[i-1]] + 1);
}else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[n][amount] >= maxs ? -1 : dp[n][amount];
}
}
解法二:动态规划-压缩数组
- 类似于其他的动态规划题目,本题可以使用压缩数组来优化存储空间。
- 具体地说,就是本题中的第一维可以不需要,而在遍历中的i-1就可以用dp本身来表示,因为当前i状态就是从i-1状态转换而来的。
- 与解法一类似,这里也需要注意边界值的转换,除了dp[0]需要赋值为0,其他所有位置均赋值为最大值。
class Solution {
public int coinChange(int[] coins, int amount) {
int n = coins.length;
// dp[i][j]表示使用前i种硬币凑成金额j需要的最小硬币数
int[] dp = new int[amount+1];
// dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i]] + 1);
int maxs = 0x3f3f3f3f;
Arrays.fill(dp, maxs);
dp[0] = 0;
for(int i=1; i<=n; i++){
for(int j=coins[i-1]; j<=amount; j++){
dp[j] = Math.min(dp[j], dp[j-coins[i-1]] + 1);
}
}
return dp[amount] >= maxs ? -1 : dp[amount];
}
}
相似题目
Either Excellent or Rusty