零钱兑换 II
问题描述
给定不同面额的硬币和一个总金额,写一个函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
问题分析
这是一个典型的动态规划问题。我们可以使用动态规划来解决这个问题。具体来说,我们可以使用一个一维数组dp
来记录目标金额为i
时的硬币组合数。数组dp
的长度为目标金额加一,初始时所有元素都为零。然后,我们遍历硬币的面额,对于每个面额coin
,我们从coin
开始遍历到目标金额amount
,并更新dp[j]
的值。更新的逻辑是,对于每个j
,dp[j]
的值等于dp[j]
和dp[j-coin]
之和,其中dp[j-coin]
表示使用硬币coin
时的组合数,dp[j]
表示不使用硬币coin
时的组合数。最终,返回dp[amount]
即可。
算法实现
下面是整个算法的流程图:
步骤 | 动作 |
---|---|
1 | 初始化dp 数组,将所有元素设置为零。 |
2 | 对于每个硬币面额coin ,执行以下步骤: |
3 | 对于i 从coin 到目标金额amount ,执行以下步骤: |
4 | 更新dp[i] 的值为dp[i] + dp[i-coin] 。 |
5 | 返回dp[amount] 的值。 |
下面是具体的代码实现:
public int change(int amount, int[] coins) {
// 初始化dp数组
int[] dp = new int[amount + 1];
dp[0] = 1;
// 遍历硬币面额
for (int coin : coins) {
// 遍历目标金额
for (int i = coin; i <= amount; i++) {
// 更新dp[i]的值
dp[i] += dp[i - coin];
}
}
// 返回dp[amount]的值
return dp[amount];
}
代码解析
首先,在算法的开始部分,我们初始化了一个一维数组dp
,将所有元素都设置为零。数组的长度为目标金额amount
加一,这是因为我们需要考虑目标金额为零的情况。然后,我们将dp[0]
的值设置为1,表示当目标金额为零时,存在一种硬币组合方式,即不使用任何硬币。
接下来,我们使用两个嵌套的循环来遍历硬币的面额和目标金额。外层循环遍历硬币面额coin
,内层循环从coin
开始遍历到目标金额amount
,并更新dp[i]
的值。更新的逻辑是,我们将dp[i]
的值加上dp[i-coin]
的值,表示使用硬币coin
时的组合数。
最后,我们返回dp[amount]
的值,即为目标金额amount
的硬币组合数。
总结
通过动态规划算法,我们可以高效地计算出给定金额的硬币组合数。这个算法的时间复杂度为O(amount * n),其中,amount为目标金额,n为硬币的面额个数。通过使用动态规划算法,我们可以避免重复计算,从而提高了算法的效率。