题目描述
给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
样例
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3。
输入:amount = 10, coins = [10]
输出:1
限制
1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins
中的所有值 互不相同。0 <= amount <= 5000
算法
(动态规划,完全背包) $O(amount \cdot n)$
- 将总金额视为背包容量,将硬币的面额视为重量,价值视为 $1$,此题就是变种的固定容量完全背包问题。
- 设状态 $f(j)$ 表示硬币总面额为 $j$ 时的方案数。
- 初始时 $f(0) = 1$。
- 对于每一种硬币面额 $coins(i)$,$j$ 从 $coins(i)$ 枚举到 $amount$,累计转移 $f(j) = f(j) + f(j - coins(i))$。
- 最终答案为 $f(amount)$。
时间复杂度
- 状态数为 $O(amount)$,转移总数为$O(n)$,每次转移的时间复杂度为 $O(1)$,故总时间复杂度为 $O(amount \cdot n)$。
C++ 代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
vector<int> f(amount + 1, 0);
f[0] = 1;
for (int i = 0; i < n; i++)
for (int j = coins[i]; j <= amount; j++)
f[j] += f[j - coins[i]];
return f[amount];
}
};
for (int j = 0; j <= amount; j )
for (int i = 0; i < coins.size(); i )
为什么外循环是遍历amount就wa了呀
标准的状态转移是考虑了前 $i$ 种硬币,总价值为 $j$ 的方案数,通过完全背包优化成了一维。如果外层循环是 $amount$,会出现重复,例如总价值为 $3$,$1+2$和 $2+1$ 就会被看做两种。
这里
f[0] = 1
表示什么含义呢?硬币总面额为 0 是只有 1 种方案,这是初始状态。
好的 谢谢。(另外多问一句,你是5:37就起床了吗!?)
我在美国hh
hh
没看懂,大佬能解释一下这个是什么思路吗?
这个是背包问题,如果没学过,可以看一下背包九讲
好的 谢谢啦 我回去看看 很晚才报的名