LeetCode 08.11硬币思路记录
最近在集中做LeetCode上的动态规划类的问题。在做题(看题解)过程中,发现了很多有意思的思路和方法。在这里做个简单的总结和梳理:
硬币。给定数量不限的硬币,币值为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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
传统的完全背包问题不在此重述,因为它的解法已经被很多大佬解释的通透了,无需本菜在此搬弄,这篇文章只对这个硬币问题梳理思路。
首先容我展示此题AC代码:
class Solution {
public:
//常量定义
const int Mod = 1000000007;
int waysToChange(int n) {
//枚举硬币集合,方便递推时取用
vector<int> CoinSet = {
1, 5, 10, 25};
//递推奠基
int DP[1000010] = {
0};
DP[0] = 1;
//递推
for(auto Coin : CoinSet)
for(int k = Coin ; k <= n ; ++k)
DP[k] = (DP[k] + DP[k - Coin]) % Mod;
//返回结果
return DP[n];
}
};
下面对这段代码逐段解释:
1.递推奠基:
首先DP[k]的含义是,当硬币总价值为k时硬币组合的方案数(更确切地说,方案总数对Mod取模)。
那么你可以想见,此题需要的返回结果一定是DP[n]。
下面是递归奠基部分的详解:
初始化时,一定要将所有方案数清零,表示此时还未开始递推。
DP[0] = 1;表示需要的总价值为0时,方案数为1(就是不给任何硬币)。
注意这里不是0。
想象一下,有人向你要0分钱。你有什么对策吗?当然有,那就是什么都不做,也不用掏钱,这就是唯一正确的处理方法。
2.递推
外层循环 for(auto Coin : CoinSet),表示你手中当前唯一持有的硬币面值。
for(auto Coin : CoinSet)
内层循环 for(int k = Coin ; k <= n ; ++k)表示对硬币总面值为k的方案数进行逐层递推。
for(int k = Coin ; k <= n ; ++k)
所以循环的意思很好理解了,因为我手里只有Coin面值的硬币,所以总面值为k-Coin时的方案数再凑上手里这枚Coin面值的硬币,就是一种新的方案。
为了让结果不溢出,我们在计算时直接取模,于是就有了这行代码:
DP[k] = (DP[k] + DP[k - Coin]) % Mod;
最终返回结果DP[n]即可