You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Note: You can assume that

  • 0 <= amount <= 5000
  • 1 <= coin <= 5000
  • the number of coins is less than 500
  • the answer is guaranteed to fit into signed 32-bit integer

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]
Output: 1

这道题是之前那道 Coin Change 的拓展,那道题问我们最少能用多少个硬币组成给定的钱数,而这道题问的是组成给定钱数总共有多少种不同的方法。还是要使用 DP 来做,首先来考虑最简单的情况,如果只有一个硬币的话,那么给定钱数的组成方式就最多有1种,就看此钱数能否整除该硬币值。当有两个硬币的话,组成某个钱数的方式就可能有多种,比如可能由每种硬币单独来组成,或者是两种硬币同时来组成,怎么量化呢?比如我们有两个硬币 [1,2],钱数为5,那么钱数的5的组成方法是可以看作两部分组成,一种是由硬币1单独组成,那么仅有一种情况 (1+1+1+1+1);另一种是由1和2共同组成,说明组成方法中至少需要有一个2,所以此时先取出一个硬币2,然后只要拼出钱数为3即可,这个3还是可以用硬币1和2来拼,所以就相当于求由硬币 [1,2] 组成的钱数为3的总方法。是不是不太好理解,多想想。这里需要一个二维的 dp 数组,其中 dp[i][j] 表示用前i个硬币组成钱数为j的不同组合方法,怎么算才不会重复,也不会漏掉呢?我们采用的方法是一个硬币一个硬币的增加,每增加一个硬币,都从1遍历到 amount,对于遍历到的当前钱数j,组成方法就是不加上当前硬币的拼法 dp[i-1][j],还要加上,去掉当前硬币值的钱数的组成方法,当然钱数j要大于当前硬币值,状态转移方程也在上面的分析中得到了:

dp[i][j] = dp[i - 1][j] + (j >= coins[i - 1] ? dp[i][j - coins[i - 1]] : 0)

注意要初始化每行的第一个位置为0,参见代码如下:

解法一:

class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<vector<int>> dp(coins.size() + , vector<int>(amount + , ));
dp[][] = ;
for (int i = ; i <= coins.size(); ++i) {
dp[i][] = ;
for (int j = ; j <= amount; ++j) {
dp[i][j] = dp[i - ][j] + (j >= coins[i - ] ? dp[i][j - coins[i - ]] : );
}
}
return dp[coins.size()][amount];
}
};

我们可以对空间进行优化,由于 dp[i][j] 仅仅依赖于 dp[i - 1][j] 和 dp[i][j - coins[i - 1]] 这两项,就可以使用一个一维dp数组来代替,此时的 dp[i] 表示组成钱数i的不同方法。其实最开始的时候,博主就想着用一维的 dp 数组来写,但是博主开始想的方法是把里面两个 for 循环调换了一个位置,结果计算的种类数要大于正确答案,所以一定要注意 for 循环的顺序不能搞反,参见代码如下:

解法二:

class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount + , );
dp[] = ;
for (int coin : coins) {
for (int i = coin; i <= amount; ++i) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
};

在 CareerCup 中,有一道极其相似的题 9.8 Represent N Cents 美分的组成,书里面用的是那种递归的方法,博主想将其解法直接搬到这道题里,但是失败了,博主发现使用那种的递归的解法必须要有值为1的硬币存在,这点无法在这道题里满足。你以为这样博主就没有办法了吗?当然有,博主加了判断,当用到最后一个硬币时,判断当前还剩的钱数是否能整除这个硬币,不能的话就返回0,否则返回1。还有就是用二维数组的 memo 会 TLE,所以博主换成了 map,就可以通过啦~

解法三:

class Solution {
public:
int change(int amount, vector<int>& coins) {
if (amount == ) return ;
if (coins.empty()) return ;
map<pair<int, int>, int> memo;
return helper(amount, coins, , memo);
}
int helper(int amount, vector<int>& coins, int idx, map<pair<int, int>, int>& memo) {
if (amount == ) return ;
else if (idx >= coins.size()) return ;
else if (idx == coins.size() - ) return amount % coins[idx] == ;
if (memo.count({amount, idx})) return memo[{amount, idx}];
int val = coins[idx], res = ;
for (int i = ; i * val <= amount; ++i) {
int rem = amount - i * val;
res += helper(rem, coins, idx + , memo);
}
return memo[{amount, idx}] = res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/518

类似题目:

Coin Change

9.8 Represent N Cents 美分的组成

参考资料:

https://leetcode.com/problems/coin-change-2/

https://leetcode.com/problems/coin-change-2/discuss/141076/Logical-Thinking-with-Clear-Java-Code

https://leetcode.com/problems/coin-change-2/discuss/99212/Knapsack-problem-Java-solution-with-thinking-process-O(nm)-Time-and-O(m)-Space

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 518. Coin Change 2 硬币找零之二的更多相关文章

  1. [LeetCode] 518. Coin Change 2 硬币找零 2

    You are given coins of different denominations and a total amount of money. Write a function to comp ...

  2. [LeetCode] Coin Change 2 硬币找零之二

    You are given coins of different denominations and a total amount of money. Write a function to comp ...

  3. 【LeetCode】518. Coin Change 2 解题报告(Python)

    [LeetCode]518. Coin Change 2 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目 ...

  4. dp算法之硬币找零问题

    题目:硬币找零 题目介绍:现在有面值1.3.5元三种硬币无限个,问组成n元的硬币的最小数目? 分析:现在假设n=10,画出状态分布图: 硬币编号 硬币面值p 1 1 2 3 3 5 编号i/n总数j ...

  5. codevs 3961 硬币找零【完全背包DP/记忆化搜索】

    题目描述 Description 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资. 我们应该 ...

  6. NYOJ 995 硬币找零

    硬币找零 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从 ...

  7. [LeetCode] Coin Change 硬币找零

    You are given coins of different denominations and a total amount of money amount. Write a function ...

  8. [LeetCode] 322. Coin Change 硬币找零

    You are given coins of different denominations and a total amount of money amount. Write a function ...

  9. [LeetCode] Lemonade Change 买柠檬找零

    At a lemonade stand, each lemonade costs $5.  Customers are standing in a queue to buy from you, and ...

随机推荐

  1. Sencha Modern

    Sencha Modern  前言         经过一个月捣鼓,基于sencha公司最新框架版本 ExtJS7.0 modern 的示例代码可以和大家见面了.示例中对系统主题.公共(组件,方法)封 ...

  2. LeetCode 232:用栈实现队列 Implement Queue using Stacks

    题目: 使用栈实现队列的下列操作: push(x) -- 将一个元素放入队列的尾部. pop() -- 从队列首部移除元素. peek() -- 返回队列首部的元素. empty() -- 返回队列是 ...

  3. Vue.js 源码分析(十五) 指令篇 v-bind指令详解

    指令是Vue.js模板中最常用的一项功能,它带有前缀v-,比如上面说的v-if.v-html.v-pre等.指令的主要职责就是当其表达式的值改变时,相应的将某些行为应用到DOM上,先介绍v-bind指 ...

  4. 什么是IDE(集成开发环境)?

    实际开发中,除了编译器是必须的工具,我们往往还需要很多其他辅助软件,例如: 编辑器:用来编写代码,并且给代码着色,以方便阅读: 代码提示器:输入部分代码,即可提示全部代码,加速代码的编写过程: 调试器 ...

  5. golang io中io.go解读

    目录 1. 整体大纲 2. 接口 读 写 关闭 寻址 3. 函数 读 写 复制 4. 结构体 SectionReader LimitedReader teeReader 5. 备注 根据golang ...

  6. C 结构体、位域

    参考链接:https://www.runoob.com/cprogramming/c-structures.html 结构体是干啥的 例如数组可以用来存储多个相同数据类型的数据项,结构体也是一种数据类 ...

  7. Java生鲜电商平台-高并发的设计与架构

    Java生鲜电商平台-高并发的设计与架构 说明:源码下载Java开源生鲜电商平台以及高并发的设计与架构文档 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中 ...

  8. Map-HashMap-遍历

    第一种遍历方法 : 先获取Map中的所有key值,然后根据key,依次从Map中去数据 (针对只取 Key 或者 Value 的情况) Map<String, String> hashMa ...

  9. .Net 高级 模拟事件模型

    第一步:创建一个类,并继承:IHttpModule using System; using System.Collections.Generic; using System.Linq; using S ...

  10. Windows 下MongoDB复制集配置

    1.下载服务.https://www.mongodb.com/   点击products 下拉第二列MongoDB server  选择 4.0.6 2.下载下来后 有限管理员运行 一路安装,可以不用 ...