LeetCode-518. 零钱兑换 II

题目来源

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. 零钱兑换

解法一:动态规划-完全背包问题

  1. 本题与背包问题极其类似,特别是看到【所有】等关键词,以及题目中提到的【假设每一种面额的硬币有无限个】更可以确信使用完全背包解决。
  2. 我们假设dp[i][j]表示用前i种硬币能拼凑出j金额的组合数,那么什么情况可以推进状态的变化呢?
    • 第一种情况是我们只选前i-1种硬币就能拼凑出j金额。
    • 第二种情况是现在只能拼凑出j-coins[i-1]金额,但是也使用了i种硬币。这种情况比较难以理解,我们可以这样想,因为每种硬币的个数都是无限的,即使我们之前使用了第i种硬币使硬币金额总数达到了j-coins[i-1],我们还是可以重复使用第i种硬币来使结果金额达到j。
  3. 综上,我们可以得出动态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j - coins[i]];
  4. 需要注意的是,以上的状态转移方程只有在j>=coins[i]时才适用,当j<coins[i]时,dp[i][j] = dp[i-1][j],即后一种情况不用考虑。
class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        // dp[i][j]表示用前i种硬币能拼凑出j金额的组合数。
        // dp[i][j] = dp[i-1][j] + dp[i][j - coins[i]];
        int[][] dp = new int[n+1][amount+1];
        for(int i=0; i <= n; i++){
            dp[i][0] = 1;
        }
        for(int i=1; i<=n; i++){
            for(int j=0; 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[n][amount];
    }
}

解法二:动态规划-压缩数组

  1. 只要碰到动态规划,我们就应该想想能否使用压缩数组进行空间的优化,一般来说都是可以的。
  2. 与传统的压缩数组类似,本题的完全背包变形问题也可以应用压缩数组优化手段。
  3. 需要注意的是,在初始化时需要将dp[0]设置为1表示,对于金额0,即使不使用任何硬币也能凑到,所以组合数为1。
class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        // dp[i][j]表示用前i种硬币能拼凑出j金额的组合数。
        // dp[i][j] = dp[i-1][j] + dp[i][j - coins[i]];
        int[] dp = new int[amount+1];
        dp[0] = 1;
        for(int i=1; i<=n; i++){
            for(int j=0; j<= amount; j++){
                if(j >= coins[i-1]){
                    dp[j] = dp[j] + dp[j - coins[i-1]];
                }
            }
        }
        return dp[amount];
    }
}

相似题目

LeetCode-279 完全平方数
LeetCode-322. 零钱兑换
LeetCode-518. 零钱兑换 II

结果展示

image

posted @ 2022-02-05 21:37  Garrett_Wale  阅读(143)  评论(0编辑  收藏  举报