5.0 KiB
思路
-
确定dp数组以及下标的含义 dp[j]:凑足总额为j所需钱币的最少个数为dp[j]
-
确定递推公式
得到dp[j](考虑coins[i]),只有一个来源,dp[j - coins[i]](没有考虑coins[i])。
凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])
所以dp[j] 要去所有 dp[j - coins[i]] + 1 中最小的。
递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
- dp数组如何初始化
首先凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0;
其他下标对应的数值呢?
考虑到递推公式的特性,dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中比初始值覆盖。
所以下标非0的元素都是应该是最大值。
代码如下:
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
- 确定遍历顺序
求钱币最小个数,那么钱币有顺序,和钱币没有顺序都可以,都不影响钱币的最小个数。可以用背包组合方式或者排列方式来求。
如果本题要是求组成amount的有几种方式,那么钱币循序就有影响了。
所以两个for循环的关系是:coins放在外循环,target在内循环、或者target放在外循环,coins在内循环都是可以的!
那么我采用coins放在外循环,target在内循环的方式。
本题钱币数量可以无限使用,那么是完全背包。所以遍历的内循环是正序
综上所述,遍历顺序为:coins放在外循环,target在内循环。且内循环正序。
C++ 代码如下:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for (int i = 0 ;i < coins.size(); i++) { // 遍历钱币
for (int j = coins[i]; j <= amount; j++) { // 遍历target
if (dp[j - coins[i]] != INT_MAX) { // 如果dp[j - coins[i]]是初始值则跳过
dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
}
}
}
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
拓展
对于遍历方式target放在外循环,coins在内循环都是可以的,只不过对应的初始化操作有点微调,我就直接给出代码了
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.size(); j++) {
if (i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX ) {
dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
}
}
}
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
总结
相信大家看网上的题解,一篇是遍历amount的for循环放外面,一篇是遍历amount的for循环放里面,看多了都看晕了,能把 遍历顺序讲明白的文章非常少。
这也是大多数同学学习动态规划的苦恼所在,有的时候递推公式其实很简单,但遍历顺序很难把握!
那么Carl就把遍历顺序分析的清清楚楚,相信大家看完之后,对背包问题又了更深的理解了。
tmp
// dp初始化很重要
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//int dp[10003] = {0}; // 并没有给所有元素赋值0
if (amount == 0) return 0; // 这个要注意
vector<int> dp(10003, 0);
// 不能这么初始化啊,[2147483647],2 这种例子 直接gg,但是这种初始化有助于理解
for (int i = 0; i < coins.size(); i++) {
if (coins[i] <= amount) // 还必须要加这个判断
dp[coins[i]] = 1;
}
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.size(); j++) {
if (i - coins[j] >= 0 && dp[i - coins[j]]!=0 ) {
if (dp[i] == 0) dp[i] = dp[i - coins[j]] + 1;
else dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
}
}
//for (int k = 0 ; k<= amount; k++) {
// cout << dp[k] << " ";
//}
//cout << endl;
}
if (dp[amount] == 0) return -1;
return dp[amount];
}
};
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//int dp[10003] = {0}; // 并没有给所有元素赋值0
if (amount == 0) return 0; // 这个要注意
vector<int> dp(amount + 1, 0);
for (int i = 1; i <= amount; i++) {
dp[i] = INT_MAX;
for (int j = 0; j < coins.size(); j++) {
if (i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX) {
dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
}
}
}
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};