版本更新
This commit is contained in:
@@ -1,155 +0,0 @@
|
||||
|
||||
# 思路
|
||||
* 确定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在内循环都是可以的,只不过对应的初始化操作有点微调,我就直接给出代码了
|
||||
|
||||
```C++
|
||||
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];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user