【0518】零钱兑换 II

题目:518. 零钱兑换 II

题目描述:

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

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/coin-change-ii

示例:

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

思路:

题目划分[动态规划-背包问题-完全背包]:
  1. 这是一道典型的背包问题,看到钱币的数量不限,就知道这是一个完全背包问题
  2. 与纯完全背包不一样的是,纯完全背包是能否凑成总⾦额,⽽本题是要求凑成总⾦额的个数
题目坑点:
  1. 题目要求金币的组合数
  2. 组合数和排列数:组合不强调元素之间的顺序,排列强调元素之间的顺序
动态规划五步曲:
  1. 确定dp数组以及下标的含义

(1)dp[i]:凑成总⾦额 i 的货币组合数为dp[i]

(2)二维dp[i] [j] 表示使用0~i金币凑成总⾦额j的货币组合数为dp[i] [j],但是可以通过滚动数组的思想变成上述一维数组dp[i]

  1. 确定递推公式

递推公式:dp[j] += dp[j - coins[i]];

含义:要求满足金额 j ,则遍历到 金币 coins[i] 时,当前组合数dp[j]就要加上满足金额j - conins[i] 时的组合数dp[j - conins[i]]。这也是背包问题里求装满背包有几种方法的一般公式。

  1. dp数组如何初始化

dp[0] = 1

含义:金额为0时本身没有组合数,但是当金额数恰好遍历到对应的物品时,也是一种组合数,便将dp[0] 设置为1,方便组合数的叠加

  1. 确定遍历顺序

这是一个老大难的问题,到底是先遍历金额再遍历金币,还是先遍历金币再遍历金额

实际上想不明白,试着试着也能出来

假如外层循环遍历金币(物品),内层循环遍历金额(背包),情况如下

for(int i = 0; i < coins.length; i++){
    for(int j = coins[i]; j <= amount; j++){
        dp[j] += dp[j - coins[i]];
    }
}
/*
假设:coins[0] = 1,coins[1] = 5。
那么在外层循环会先把1加⼊计算,然后再把5加⼊计算,得到的⽅法数量只有{1, 5}这种情况。⽽不会出现{5, 1}的情况。
我们得到的就是**组合数**
*/

假如外层循环遍历金额(背包),内层循环遍历金币(物品),情况如下
(相关题目377. 组合总和 Ⅳ)

for(int j = 1; j <= amount; j++){
    for(int i = 0; i < coins.length; i++){
        if(j - coins[i] >= 0)
            dp[j] += dp[j - coins[i]];
    }
}
/*
这种情况下,经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。
例如:金额为6,遇到金币1,会加上dp[5]的情况,遇到金币5,会加上dp[1]的情况
导致得到的是**排列数**
*/

5.举例推导dp数组

根据上述分析,推导几个dp数组中的值,对比理论和实际的情况是否吻合

代码如下:

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        // 1.凑成总⾦额 i 的货币组合数为dp[i]
        int[] dp = new int[amount + 1];
        // 2.确定递推公式
        // 3.初始化
        dp[0] = 1;
        // 4.确定遍历顺序
        for(int i = 0; i < n; i++){ // 金币(物品)
            for(int j = coins[i]; j <= amount; j++){// 金额(背包重量)
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摸鱼波纹疾走

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值