要求:
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例:
输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。
输入: amount = 10, coins = [10]
输出: 1
思路:
- 状态:两个,背包容量和可选择的物品。
- 选择:装进背包或不装进背包。如果不把coins[i]装进背包,那么dp[i][j]=dp[i-1][j]。如果把第i个物品装进背包,dp[i][j]=dp[i][h-coins[i-1]]。coins[i-1]表示的其实是第i个硬币。所以dp[i][j]其实是两种选择之和。
- dp数组的定义:dp[i][j]代表若只使用前i个物品,当背包容量为j时,有dp[i][j]种方法可以装满背包。
- base case:dp[0][...]=0, dp[...][0]=1;不使用硬币,无法凑出任何硬币。目标金额为0,必只有一种选择,什么都不选。
- 返回值:dp[N][amount],N为coins数组大小
模板:
int dp[N][amount+1]
dp[0][...]=0
dp[...][0]=1
for i in [1...N]:
for j in [1...amount]:
把物品i装进背包
不把物品i放进背包
return dp[N][amount]
代码:
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = new int[n+1][amount+1];
for (int i = 0; i < n+1; i++) {
dp[i][0] = 1;
}
for (int i = 1; i < n+1; i++) {
for (int j = 1; j < amount+1; j++) {
if (j-coins[i-1] >= 0) {//背包容量装得下
dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
} else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[n][amount];
}
}