给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 1:
输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
示例 2:
输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。
示例 3:
输入: amount = 10, coins = [10]
输出: 1
题意:
很明显,这是一道动态规划题。定义一个dp数组,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为前
i
i
i 个硬币,凑成面值为
j
j
j 的方案数量。为方便初始化,我们定义
d
p
[
0
]
[
j
]
dp[0][j]
dp[0][j] 代表不考虑任何硬币,即使用0个硬币时。
则很显然,
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0] = 1,其他情况
d
p
[
0
]
[
j
]
dp[0][j]
dp[0][j] = 0。代表 0 个硬币凑齐面值为 0 的情况有一种,凑成其他面值的情况一种也没有。
当「状态定义」与「基本初始化」有了之后,我们不失一般性的考虑 f[i][j]f[i][j] 该如何转移。
对于第 ii 个硬币我们有两种决策方案:
不使用该硬币:
f[i - 1][j]
f[i−1][j]
使用该硬币:由于每个硬币可以被选择多次(容量允许的情况下),因此方案数量应当是选择「任意个」该硬币的方案总和:
∑
k
=
1
⌊
j
/
v
a
l
⌋
f
[
i
−
1
]
[
j
−
k
∗
v
a
l
]
,
v
a
l
=
n
u
m
s
[
i
−
1
]
\sum_{k = 1}^{\left \lfloor {j / val} \right \rfloor}f[i - 1][j - k * val], val = nums[i - 1]
k=1∑⌊j/val⌋f[i−1][j−k∗val],val=nums[i−1]
代码:
class Solution {
public int change(int amount, int[] coins) {
//定义dp数组
int dp[][] = new int[coins.length + 1][amount + 1];
//当总金额数为0时,此时一枚硬币也不拿
//这时方案数为1
dp[0][0] = 1;
//动态规划
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
//当选取 i 枚硬币,凑齐 j 面值时,至少也有dp[i-1][j]种方案
dp[i][j] += dp[i - 1][j];
//计算新增的硬币面值能够增加几种方案
for (int k = 1; j - k * coins[i - 1] >= 0; k++) {
dp[i][j] += dp[i-1][j-k*coins[i-1]];
}
}
}
//返回当使用所有面值硬币时,且凑齐了 amount 面值的方案数
return dp[dp.length-1][dp[0].length-1];
}
}