友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

518. 零钱兑换 II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 `amount`表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 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

提示:

  • 1 <= coins.length <= 300

  • 1 <= coins[i] <= 5000

  • coins 中的所有值 互不相同

  • 0 <= amount <= 5000

思路分析

一道动态规划,是 322. 零钱兑换 的延伸题目。

假设 dp[i][c] 表示使用前 i 个硬币表示金额为 c 的方案。那么对于当前的硬币 i,有两个选择:

  • 选用:\(dp[i][c] = dp[i][c - coin[i]]\)。注意:由于硬币可以重复使用,即使选用,也要从 i 开始向前递推。

  • 不选:\(dp[i][c] = dp[i-1][c]\)。不选,则从 i - 0 开始向前递推。

那么,根据加法原理, \(dp[i][c] = dp[i-1][c] + dp[i][c - coin[i]]\)

注意:不同嵌套顺序表示不同意义。

先金额后硬币,会导致重复计数:

0518 10

先硬币再金额,就可以避免重复计数的问题:

0518 11
  • 一刷

  • 一刷(优化)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * @author D瓜哥 · https://www.diguage.com
 * @since 2025-10-10 16:56:01
 */
public int change(int amount, int[] coins) {
  int[][] dp = new int[coins.length + 1][amount + 1];
  dp[0][0] = 1;
  for (int i = 0; i < coins.length; i++) {
    for (int j = 0; j <= amount; j++) {
      if (coins[i] > j) {
        dp[i + 1][j] = dp[i][j];
      } else {
        dp[i + 1][j] = dp[i][j] + dp[i + 1][j - coins[i]];
      }
    }
  }
  return dp[coins.length][amount];
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * @author D瓜哥 · https://www.diguage.com
 * @since 2025-10-10 09:41:28
 */
public int change(int amount, int[] coins) {
  int[] dp = new int[amount + 1];
  dp[0] = 1;
  for (int coin : coins) {
    for (int i = coin; i <= amount; i++) {
      dp[i] += dp[i - coin];
    }
  }
  return dp[amount];
}