作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/coin-lcci/solution/ying-bi-by-leetcode-solution/
硬币
题目描述
给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
题解
方法一:数学分析
复杂度分析:
时间复杂度: 这里只使用了一重循环,循环的次数为
⌊
n
25
⌋
\lfloor \frac{n}{25} \rfloor
⌊25n⌋,所以渐进时间复杂度为
O
(
⌊
n
25
⌋
)
=
O
(
n
)
O(\lfloor \frac{n}{25} \rfloor) = O(n)
O(⌊25n⌋)=O(n)。
空间复杂度: 没有使用辅助数组,只用了常数个临时变量(与问题规模 n 无关),故渐进空间复杂度为
O
(
1
)
O(1)
O(1)。
方法二:动态规划
其实这个问题是两个非常经典的问题的组合,其一是 「完全背包问题」,其二是 「背包方案数问题」,如果读者感兴趣的话可以自行阅读《背包九讲》。
复杂度分析
时间复杂度: 上文分析过,方程化简之后的渐进时间复杂度为
O
(
4
×
(
n
+
1
)
)
=
O
(
n
)
O(4 \times (n + 1)) = O(n)
O(4×(n+1))=O(n)。
空间复杂度: 使用滚动数组思想优化后,只需要使用一个长度为
n
+
1
n + 1
n+1 的一维数组,故渐进空间复杂度为
O
(
n
)
O(n)
O(n)。
代码
class Solution {
public:
int waysToChange(int n) {
/*
//方法一:数学分析:枚举25分硬币,然后等差数列求和
int ans = 0;
for(int i = 0; i * 25 <= n; ++i){
int rest = n - i * 25;
int a = rest / 10;
int b = rest % 10 / 5;
ans = (ans + (long long)(a + 1) * (a + b + 1) % mod) % mod;//确保不会溢出
}
return ans;
*/
//方法二:动态规划--完全背包问题 + 背包方案数问题
vector<int> f(n+1);
f[0] = 1;
// 注意这里的循环嵌套次序不可颠倒!
for(int c = 0; c < 4; ++c){
int coin = coins[c];
for(int i = coin; i <= n; ++i){
f[i] = (f[i] + f[i-coin]) % mod;
}
}
return f[n];
}
private:
//1000000007 是最小的十位质数。模 1000000007 ,可以保证值永远在int的范围内。
static constexpr int mod = 1000000007;
//constexpr variables are not compile-time values 在编译时初始化
static constexpr int coins[4] = {25, 10, 5, 1};
};
小结
背包问题
泛指以下这一种问题:
给定一组有固定价值和固定重量的物品,以及一个已知最大承重量的背包,求在不超过背包最大承重量的前提下,能放进背包里面的物品的最大总价值。
这一类问题是典型的使用动态规划解决的问题,我们可以把背包问题分成3种不同的子问题:0-1背包问题、完全背包和多重背包问题。
动态规划
初始状态和状态转移方程