题目
代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
}
};
方法一:二维动态规划
分析
设定二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示使用前编号 i i i 的硬币中,可以得到总金额为 j j j 的组合数,二维数组 d p [ 0 ] [ 0 ] = 1 dp[0][0] = 1 dp[0][0]=1,其余起始值均为 0 0 0。对与编号 i i i 的硬币有选和不选两种情况:
- 如果不选择使用编号 i i i 的硬币时,只是用前 i − 1 i-1 i−1 个硬币可以得到总金额为 j j j 的组合数为: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i−1][j]。
- 如果选择使用编号
i
i
i 的硬币时,由于硬币可以重复使用,设定可以使用
k
k
k 个编号
i
i
i 的硬币,则可以得到总金额为
j
j
j 的组合数为:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
j
−
k
×
c
o
i
n
s
[
i
]
]
dp[i][j] = dp[i-1][j] + dp[i-1][j-k\times coins[i]]
dp[i][j]=dp[i−1][j]+dp[i−1][j−k×coins[i]]。
综上所述 d p [ i ] [ j ] dp[i][j] dp[i][j] 的状态转移方程可表示为:
d p [ i ] [ j ] = { d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − k × c o i n s [ i ] ] i f j ≥ k × c o i n s [ i ] & k ∈ N ∗ d p [ i ] [ j ] = d p [ i − 1 ] [ j ] o t h e r s dp[i][j] = \begin{cases} dp[i][j]=dp[i-1][j] + dp[i-1][j-k\times coins[i]] &if\ j \geq k\times coins[i]\ \&\ k \in N^*\\ dp[i][j] = dp[i-1][j] &others \end{cases} dp[i][j]={dp[i][j]=dp[i−1][j]+dp[i−1][j−k×coins[i]]dp[i][j]=dp[i−1][j]if j≥k×coins[i] & k∈N∗others d p [ n ] [ a m o u n t ] dp[n][amount] dp[n][amount] 即为问题答案。
要点
该题目属于组合问题,硬币可重复使用
代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
vector<vector<int>> dp (n + 1, vector<int>(amount + 1));
dp[0][0] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j <= amount; ++j) {
dp[i][j] = dp[i - 1][j];
for (int k = 1; k * coins[i - 1] <= j; k++) {
dp[i][j] += dp[i - 1][j - k * coins[i - 1]];
}
}
}
return dp[n][amount];
}
};
复杂度分析
- 时间复杂度: O ( n × a m o u n t ) O(n \times amount) O(n×amount),其中 n n n 是硬币种类, a m o u n t amount amount 是所需总金额。
- 空间复杂度: O ( n × a m o u n t ) O(n \times amount) O(n×amount),其中 n n n 是硬币种类, a m o u n t amount amount 是所需总金额,实现动态规划需要创建 n × a m o u n t n \times amount n×amount 的二维数组 d p dp dp。
方法二:一维动态规划
分析
该题目是一道典型的组合数问题,设定一维数组
d
p
[
i
]
dp[i]
dp[i] 表示构成总金额
i
i
i 的硬币组合数,当
i
=
0
i = 0
i=0 时,只有一种情况,即
d
p
[
i
]
=
0
dp[i] = 0
dp[i]=0。我们可以先使用单一面值硬币
c
o
i
n
coin
coin 来进行计算,依次遍历所有面值的硬币,状态转移方程可表示为:
d
p
[
i
]
=
d
p
[
i
]
+
d
p
[
i
−
c
o
i
n
]
dp[i] = dp[i] + dp[i -coin]
dp[i]=dp[i]+dp[i−coin] 问题答案为:
d
p
[
a
m
o
u
n
t
]
dp[amount]
dp[amount]
代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp (amount + 1);
dp[0] = 1;
for(int& coin : coins) {
for(int i = coin; i <= amount; ++i) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
};
复杂度分析
- 时间复杂度: O ( a m o u n t × n ) O(amount \times n) O(amount×n),其中 n n n 是硬币种类, a m o u n t amount amount 是所需总金额。
- 空间复杂度: O ( a m o u n t ) O(amount) O(amount)。