leetcode-518-零钱兑换II

零钱兑换 II

题面

leetcode题目

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

example

输入: \(amount = 5\), \(coins = [1, 2, 5]\)
输出: \(4\)
解释: 有四种方式可以凑成总金额:

  • \(5=5\)
  • \(5=2+2+1\)
  • \(5=2+1+1+1\)
  • \(5=1+1+1+1+1\)

数据范围

  • \(0 \leq amount \leq 5000\)
  • \(1 \leq coin \leq 5000\)
  • 硬币种类不超过 \(500\)
  • 结果符合 \(32\) 位符号整数

题解

总体思路

很简单的dp了,考虑\(dp[i][j]\)表示选择完第\(i\)种硬币后能凑出目标金额\(j\)的组合数,那么有: \[ dp[i][j] = \sum_{k=0}^{\frac{j}{coins[i]}} dp[i-1][j - k*coins[i]] \qquad j-k*coins[i] \geq 0 \tag{1}\] \((1)\)式可以这样理解,在决定第\(i\)种硬币时,可以考虑取\(k\)枚这种硬币,\(k\)的取值范围为\(0 \leq k \leq \frac{target}{coins[i]}\),其中\(target\)为目标凑出的金额数,并且需要满足\(j-k*coins[i] \geq 0\)
这里可以用滚动数组优化一下空间。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define _DEBUG
#define LL long long

class Solution
{
public:
int change(int amount, vector<int> &coins)
{
int len = coins.size();
int dpPre[amount + 1];
int dp[amount + 1];
memset(dpPre, 0, sizeof(dpPre));
memset(dp, 0, sizeof(dp));
dpPre[0] = 1;
for (int i = 0; i < len; i++)
{
int coin = coins[i];
int max = amount / coin;
for (int j = 0; j \leq amount; j++)
{
int div = j / coin;
for (int k = 0; k \leq div && j - coin * k >= 0; k++)
{
dp[j] += dpPre[j - coin * k];
}
#ifdef _DEBUG
if (dp[j] > 0)
{
cout << "i: " << i << " j: " << j << " dp[i][j]: " << dp[j] << endl;
}
#endif
}
memcpy(dpPre, dp, sizeof(dp));
memset(dp, 0, sizeof(dp));
}
return dpPre[amount];
}
};

// for test
int main()
{
Solution s;
vector<int> t1{1, 2, 5};
cout << s.change(5, t1) << endl;

return 0;
}
作者

Yu Leng

发布于

2021-06-10

更新于

2024-10-28

许可协议

评论