【中等】面试题 08.11-硬币
题目
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例1:
输入: n = 5
输出:2
解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:
输入: n = 10
输出:4
解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-lcci
解法
方法一:计算
解题思路
对于总价值n,当选择coin25
个25面值硬币时,剩下rest=n-25*coin25
就由剩下3种面值组成,当有coin10
个10面值硬币时,剩下rest-10*coin10
的价值就最多包含(rest-10*coin10)/5
个5面值硬币,也就是(rest-10*coin10)/5+1
种方法。
所以,当选择i个25面值硬币时,剩下的组成方法就是\(\sum_{a=0}^{coin10}(rest-10*a)/5+1\)
代码
class Solution {
public:
int waysToChange(int n) {
int coin25 = n/25;
int res = 0;
for(int i = coin25; i >= 0; --i){
int rest = n-(i*25);
int coin10 = rest / 10;
res = (res + (long((rest)/5+1-coin10)*(coin10+1)%1000000007))%1000000007;
}
return res;
}
};
方法二:动态规划
解题思路
思路1:直接分析
首先,如果硬币面值只有1的话,无论总价值多大,选取的方法一定只是1,因为每次只能选择面值为1的硬币,所以count[n]=count[n-1]=1;
如果新增一个5的硬币,当总价值n不大于5时,仍旧只能选择1的硬币,但是当不小于5时,如果手里已经有了n-5的硬币,那么除了保持全选1的方法之外,还可以直接拿一个面值为5的硬币,也就是说,如果总价为n-5时有t种方法,那么总价为n时,就新增了t种方法,对于其他的面值也是一样,如果新增面值为coin,那么状态转移方程即为count[n] = count[n] + count[n-coin]
思路2:动态规划套用
首先,找状态量,题目问什么什么就是状态,题目问的是4种硬币组成价值n的方法,包含三个物理量:硬币集合/价值/方法数,其中,方法数是优化方向,价值是有限有序状态,硬币集合也是有限有序状态,所以,想到使用硬币集合大小4*价值集合大小n的数组,也就是count[i][j]表示前i个硬币构成价值j的方法数
下一步寻找状态转移方程,首先执行搜索顺序一定是逐渐增加i后逐渐增加j,就是说对于任意count[i][j],count[0...i-1][0...n]和count[0...i][j]是已知的,所以如果已知count[i][j-coin[i]]以及coin[i],那么count[i][j]种方法除了已知的count[i-1][j]种方法之外,一定包含count[i][j-coin[i]]种方法,也就是count[i][j] = count[i-1][j] + count[i][j-coin[i]]
代码
class Solution {
public:
int waysToChange(int n) {
vector<int> count(n+1, 1); // 只有1时有一种拿法
int coin[4] = {5,10,25};
for(int i = 0; i < 3; ++i){
for(int j = coin[i]; j < n+1; ++j){
count[j] = (count[j]+count[j-coin[i]])%1000000007; // 新增了新的面值
}
}
return count[n];
}
};