本题为完全背包问题,遍历容量需要顺序遍历
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 完全背包 顺序遍历
// 背包容量为amount
int n = coins.size();
// dp[i][j]:有coin[0]...coin[i]这i+1个硬币,凑出amount,有dp[i][j]种方法
vector<vector<int>> dp(n, vector<int>(amount + 1, 0));
dp[0][0] = 1;
for(int j = 1; j <= amount; j++){
// 只有coins[0]一枚硬币,若能整除,则只有1种方法凑出j,若不能整除,则不能凑出j
dp[0][j] = (j % coins[0] == 0) ? 1 : 0;
}
// 遍历硬币
for(int i = 1; i < n; i++){
// 遍历面值
for(int j = 0; j <= amount; j++){
if(j < coins[i]){
// 当前面值 < 当前硬币,无法使用当前硬币,依然使用前0~i-1种硬币凑出面值j
dp[i][j] = dp[i-1][j];
}else{
// 当前面值 >= 当前硬币
// dp[i-1][j]表示不使用当前硬币凑出j的方法数
// dp[i][j-coins[i]]则表示使用至少使用一枚coins[i]硬币凑出j,完全背包,使用本层计算结果
// 这里不能是dp[i-1][j-coins[i]],因为这表示只使用一枚coins[i]凑出面值j
dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]];
}
}
}
return dp[n-1][amount];
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 完全背包 顺序遍历
// 背包容量为amount
int n = coins.size();
// dp[j]:使用前i钟货币凑出j,有dp[j]种方法
vector<int> dp(amount + 1, 0);
dp[0] = 1;
// 遍历硬币
for(int i = 0; i < n; i++){
// 遍历面值
for(int j = coins[i]; j <= amount; j++){
dp[j] = dp[j] + dp[j-coins[i]];
}
}
return dp[amount];
}
};
01背包 —> 逆序
完全背包 —> 顺序
组合 —> 先遍历物品
排列 —> 先遍历容量
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
// 完全背包---> 顺序
// 组合 ---> 先遍历物品
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for(int i = 0; i < coins.size(); i++){
for(int j = coins[i]; j <= amount; j++){
// 不使用coins[i],使用coins[i]
// 用dp[j-coins[i]]个硬币凑出j-coins[i],再用1个coins[i]凑出coins[i]
// dp[j-coins[i]]在二维数组中使用的是本层数据,表示使用0...i种货币凑出j-coins[i],再用1个coins[i]凑出coins[i]
// 这样dp[j-coins[i]]+1就能表示至少使用1个coins[i],凑出j,所需要的货币数量
dp[j] = min(dp[j], dp[j-coins[i]] + 1);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
};