解题思路
原题链接:322. 零钱兑换
解题思路
(1)二维数组
- 动态规划五步曲:
(1)dp[i][j]含义: 在总额为j的前提下,从coins[0]-coins[i]中选择硬币,可用最少硬币组成的j的硬币个数。
(2)递推公式: dp[i][j] = min(dp[i - 1][j], dp[i][j - coins[i]]),完全背包递推公式取最小值
(3)dp数组初始化: 因为要求的是最小值,因此dp[i][0] = 0, 表示为总价值为0时什么都不选,其余都初始化为INT_MAX。当只有找当j - coins[i]
为0的数,才会因为min()
更新,如果减去的不为0,或者减去到之前没有被更新过的数,则还会保持INT_MAX。
(4)遍历顺序: 因为只要找到某种最小值的方案取的是MIN,因此先背包后物品,先物品后背包都可以。
(5)举例: (省略)
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int n = coins.size();
// 因为需要都初始化为INT_MAX,数组用memset都会转化成8bit无符号数,不方便
vector<vector<int>> dp(n + 1, vector<int>(amount + 1,INT_MAX));
for(int i = 0; i <= n; i++) dp[i][0] = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= amount; j++) {
if(coins[i - 1] <= j && dp[i][j - coins[i - 1]] != INT_MAX) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
// 当为amount时为INT_MAX,说明在递推公式时二者都没有可以对应拼凑出的结果,返回-1
return dp[n][amount] == INT_MAX ? -1 : dp[n][amount];
}
};
Python
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n = len(coins)
dp = [[inf] * (amount + 1) for _ in range(n + 1)]
for i in range(n + 1):
dp[i][0] = 0
for i in range(n + 1):
for j in range(amount + 1):
if coins[i - 1] > j:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1)
return -1 if dp[n][amount] == inf else dp[n][amount]
(2)一维滚动数组
将递推公式转化为等价式dp[j] = min(dp[j], dp[j - coins[i - 1]] + 1)
,初始化与之相同,遍历顺序按完全背包滚动数组方式从小到大。
举例:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int n = coins.size();
// 因为需要都初始化为INT_MAX,数组用memset都会转化成8bit无符号数,不方便
vector<int> dp(amount + 1,INT_MAX);
dp[0] = 0;
for(int i = 1; i <= n; i++) {
for(int j = coins[i - 1]; j <= amount; j++) {
if(dp[j - coins[i - 1]] != INT_MAX) {
dp[j] = min(dp[j], dp[j - coins[i - 1]] + 1);
}
}
}
return dp[amount] == INT_MAX ? -1 : dp[amount];
}
};
参考文章:322. 零钱兑换