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] Coin Change 2 硬币找零之二的更多相关文章

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

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

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

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

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

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

  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. NYOJ995硬币找零(简单dp)

    /* 题意:给你不同面额的硬币(每种硬币无限多),需要找零的面值是T,用这些硬币进行找零, 如果T恰好能被找零,输出最少需要的硬币的数目!否则请输出剩下钱数最少的找零方案中的最少硬币数! 思路:转换成 ...

随机推荐

  1. java并发编程基础 --- 7章节 java中的13个原子操作类

    当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量 i=1,A线程更新 i+1,B线程也更新 I+1,经过两个线程的操作之后可能 I不等于3,而是等于2.因为A和B线程更 ...

  2. Comparable接口和Comparator接口

    1.一个类在设计之初就要实现对该类对象的排序功能,那么这个类要实现Comparable接口,实现public int compareTo(T t)方法.如代码中的Student类.对于实现Compar ...

  3. Java基础学习笔记九 Java基础语法之this和super

    构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法.当 ...

  4. hibernate框架学习笔记5:缓存

    缓存不止存在与程序中,电脑硬件乃至于生活中都存在缓存 目的:提高效率 比如IO流读写字节,如果没有缓存,读一字节写一字节,效率低下 hibernate中的一级缓存:提高操作数据库的效率 示例: 抽取的 ...

  5. 《团队-OldNote-项目总结》

    我们小组做的是手机便签的app---OldNote 最开始我们想解决普通手机便签无法进行语音和照片的备忘这一问题,但是由于没有做过拍照和录音的经验怕由于技术原因无法达成目的,就没敢写在需求分析中.当完 ...

  6. C语言--第三周作业

    一.PTA作业中4个题目 1.7-9 A乘以B 要求:输入的两个整数:A是你学号前两位数字,B是你学号后两位数字 a.代码 #include <stdio.h> int main () { ...

  7. JAVA使用和操作properties文件

    java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式,在properti ...

  8. Raid 5数据恢复原理以及raid 5数据恢复实际操作案例

    Raid 5数据恢复算法原理 要理解 raid 5数据恢复原理首先要先认识raid5,"分布式奇偶校验的独立磁盘结构"也就是我们称之为的raid 5数据恢复有一个概念需要理解,也就 ...

  9. Ubuntu安装使用latex

    TeX Live is a TeX distribution to get up and running with the TeX document production system. To ins ...

  10. 从集合的无序性看待关系型数据库中的"序"

    本文目录:1.集合的特征2.集合的无序性3.表中记录的无序性4.集合的"序"和物理存储顺序之间的关系5.查询结果(虚拟表)的无序性.随机性6.为什么总是强调"无序&quo ...