难度中等238
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
我们可以把这个问题转化为背包问题的描述形式:
有一个背包,最大容量为
amount
,有一系列物品coins
,每个物品的重量为coins[i]
,每个物品的数量无限。请问有多少种方法,能够把背包恰好装满
若只使用 coins
中的前 i
个硬币的面值,若想凑出金额 j
,有 dp[i][j]
种凑法。
dp[0][..] = 0, dp[..][0] = 1(如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。)
如果你不把这第 i
个物品装入背包,也就是说你不使用 coins[i]
这个面值的硬币,那么凑出面额 j
的方法数 dp[i][j]
应该等于 dp[i-1][j]
,继承之前的结果。
如果你把这第 i
个物品装入了背包,也就是说你使用 coins[i]
这个面值的硬币,那么 dp[i][j]
应该等于 dp[i][j-coins[i-1]]
。
首先由于 i
是从 1 开始的,所以 coins
的索引是 i-1
时表示第 i
个硬币的面值。
class Solution {
public int change(int amount, int[] coins) {
int len = coins.length;
int[][] dp=new int[len+1][amount+1];
for (int i=0;i<=len;i++)
dp[i][0] =1 ;
for(int i=1;i<=len;i++)
for (int j=1;j<=amount;j++){
if (j>=coins[i-1]){
dp[i][j] =dp[i-1][j] + dp[i][j-coins[i-1]];
}
else
dp[i][j] =dp[i-1][j] ;
}
return dp[len][amount];
}
}
优化:
将二维数组压缩成一维数组 DP
公式为 amount = x: dp[x] = dp[x] + dp[x - coin],
dp[0] = 1;
不同于01背包的是 dp[i][j] =dp[i-1][j] + dp[i][j-coins[i-1]];
需要已经更新的dp[i][j-coins[i-1]]。那么遍历的时候就可以从前往后遍历
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int x = coin; x < amount + 1; ++x) {
dp[x] += dp[x - coin];
}
}
return dp[amount];
}
}
参考链接:
https://leetcode-cn.com/problems/coin-change-2/solution/dong-tai-gui-hua-wan-quan-bei-bao-wen-ti-by-liweiw/
labuladong的算法小抄: https://labuladong.gitbook.io/algo/dong-tai-gui-hua-xi-lie/bei-bao-ling-qian
背包九讲 :https://raw.githubusercontent.com/tianyicui/pack/master/V2.pdf