[LeetCode] Coin Change 2 硬币找零之二
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
类似题目:
参考资料:
https://leetcode.com/problems/coin-change-2/
https://leetcode.com/problems/coin-change-2/discuss/141076/Logical-Thinking-with-Clear-Java-Code
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Coin Change 2 硬币找零之二的更多相关文章
- [LeetCode] 518. Coin Change 2 硬币找零之二
You are given coins of different denominations and a total amount of money. Write a function to comp ...
- [LeetCode] 518. Coin Change 2 硬币找零 2
You are given coins of different denominations and a total amount of money. Write a function to comp ...
- [LeetCode] Lemonade Change 买柠檬找零
At a lemonade stand, each lemonade costs $5. Customers are standing in a queue to buy from you, and ...
- dp算法之硬币找零问题
题目:硬币找零 题目介绍:现在有面值1.3.5元三种硬币无限个,问组成n元的硬币的最小数目? 分析:现在假设n=10,画出状态分布图: 硬币编号 硬币面值p 1 1 2 3 3 5 编号i/n总数j ...
- codevs 3961 硬币找零【完全背包DP/记忆化搜索】
题目描述 Description 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资. 我们应该 ...
- NYOJ 995 硬币找零
硬币找零 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从 ...
- [LeetCode] Coin Change 硬币找零
You are given coins of different denominations and a total amount of money amount. Write a function ...
- [LeetCode] 322. Coin Change 硬币找零
You are given coins of different denominations and a total amount of money amount. Write a function ...
- NYOJ995硬币找零(简单dp)
/* 题意:给你不同面额的硬币(每种硬币无限多),需要找零的面值是T,用这些硬币进行找零, 如果T恰好能被找零,输出最少需要的硬币的数目!否则请输出剩下钱数最少的找零方案中的最少硬币数! 思路:转换成 ...
随机推荐
- 每天学习点--------第六天(2017-10-10) 摘要: mysql和Oracle的区别
1.自动增长数据类型的处理 Mysql有自动增长的数据类型,插入记录时不用操作此字段,会自动获取数据值.Oracle没有自动增长的数据类型,需要建立一个自动增长的序列号,插入记录时要把序列号的下一个值 ...
- 发布利用 Qt Assistant来做帮助系统的程序遇到的问题
最近,一直在做反演初始模型可视化建模的软件 model Constraint,最后的步骤就是利用 Qt Assistant为自己的程序制作帮助系统. 1.<Qt Creator快速入门>和 ...
- oracle创建表空间、创建用户并赋予权限
分开执行如下sql语句 --创建临时表空间 CREATE SMALLFILE TEMPORARY TABLESPACE "TEMP11" TEMPFILE 'E:\app\MD\o ...
- C语言第一周作业
题目一:7-3 温度转换 本题要求编写程序,计算华氏温度150°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1.实验代码 2.设计 ...
- 简单的C语言编译器--语法分析器
语法分析算是最难的一部分了.总而言之,语法分析就是先设计一系列语法,然后再用设计好的语法去归约词法分析中的结果.最后将归约过程打印出来,或者生成抽象语法树. 1. 设计文法 以下是我的文法(引入的 ...
- codevs 3342 绿色通道
codevs 3342 绿色通道 http://codevs.cn/problem/3342/ 难度等级:黄金 题目描述 Description <思远高考绿色通道>(Green Pass ...
- fabric.js和高级画板
本文介绍fabric.js框架使用,以及使用fabricjs打造一个高级画板程序. 高级画板功能介绍 全局绘制颜色选择 护眼模式.网格模式切换 自由绘制 画箭头 画直线 画虚线 画圆/椭圆/矩形/直角 ...
- python 面向对象之封装与类与对象
封装 一,引子 从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,小老虎一起装进麻袋,然后把麻袋封上口子.照这种逻辑看,封装='隐藏',这种理解是相当片面的 二,先看如何隐藏 在 ...
- ASP.NET Web API编程——路由
路由过程大致分为三个阶段: 1)请求URI匹配已存在路由模板 2)选择控制器 3)选择操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定义路由,例如模板默认生 ...
- Mybatis的mapper代理开发dao方法
看完了之前的mybatis原始的dao开发方法是不是觉得有点笨重,甚至说没有发挥mybatis 作为一个框架的优势.总结了一下,原始的dao方法有以下几点不足之处 dao接口实现方法中存在大量的模板方 ...