leetcode-master/problems/周总结/20210204动规周末总结.md

203 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 本周小结!(动态规划系列五)
## 周一
[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)中给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数(顺序不同的序列被视作不同的组合)。
题目面试虽然是组合,但又强调顺序不同的序列被视作不同的组合,其实这道题目求的是排列数!
递归公式dp[i] += dp[i - nums[j]];
这个和前上周讲的组合问题又不一样,关键就体现在遍历顺序上!
在[动态规划518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ) 中就已经讲过了。
**如果求组合数就是外层for循环遍历物品内层for遍历背包**
**如果求排列数就是外层for遍历背包内层for循环遍历物品**
如果把遍历nums物品放在外循环遍历target的作为内循环的话举一个例子计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合因为nums遍历放在外层3只能出现在1后面
所以本题遍历顺序最终遍历顺序:**target背包放在外循环将nums物品放在内循环内循环从前到后遍历**。
```C++
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int i = 0; i <= target; i++) { // 遍历背包
for (int j = 0; j < nums.size(); j++) { // 遍历物品
if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) {
dp[i] += dp[i - nums[j]];
}
}
}
return dp[target];
}
};
```
## 周二
爬楼梯之前我们已经做过了,就是斐波那契数列,很好解,但[动态规划70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)中我们进阶了一下。
改为:每次可以爬 1 、 2、.....、m 个台阶。问有多少种不同的方法可以爬到楼顶呢?
1阶2阶.... m阶就是物品楼顶就是背包。
每一阶可以重复使用例如跳了1阶还可以继续跳1阶。
问跳到楼顶有几种方法其实就是问装满背包有几种方法。
**此时大家应该发现这就是一个完全背包问题了!**
和昨天的题目[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)基本就是一道题了,遍历顺序也是一样一样的!
代码如下:
```C++
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; i++) { // 遍历背包
for (int j = 1; j <= m; j++) { // 遍历物品
if (i - j >= 0) dp[i] += dp[i - j];
}
}
return dp[n];
}
};
```
代码中m表示最多可以爬m个台阶代码中把m改成2就是本题70.爬楼梯可以AC的代码了
## 周三
[动态规划322.零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)给定不同面额的硬币 coins 和一个总金额 amount编写一个函数来计算可以凑成总金额所需的最少的硬币个数每种硬币的数量是无限的)。
这里我们都知道这是完全背包
递归公式dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
关键看遍历顺序
本题求钱币最小个数**那么钱币有顺序和没有顺序都可以都不影响钱币的最小个数。**。
所以本题并不强调集合是组合还是排列
**那么本题的两个for循环的关系是外层for循环遍历物品内层for遍历背包或者外层for遍历背包内层for循环遍历物品都是可以的**
外层for循环遍历物品内层for遍历背包
```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++) { // 遍历背包
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];
}
};
```
外层for遍历背包内层for循环遍历物品
```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];
}
};
```
## 周四
[动态规划279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)给定正整数 n找到若干个完全平方数比如 1, 4, 9, 16, ...使得它们的和等于 n你需要让组成和的完全平方数的个数最少平方数可以重复使用)。
如果按顺序把前面的文章都看了这道题目就是简单题了 dp[i]的定义递推公式初始化遍历顺序都是和[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ) 一样一样的
要是没有前面的基础上来做这道题那这道题目就有点难度了
**这也体现了刷题顺序的重要性**
先遍历背包在遍历物品
```C++
// 版本一
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i <= n; i++) { // 遍历背包
for (int j = 1; j * j <= i; j++) { // 遍历物品
dp[i] = min(dp[i - j * j] + 1, dp[i]);
}
}
return dp[n];
}
};
```
先遍历物品,在遍历背包:
```C++
// 版本二
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i * i <= n; i++) { // 遍历物品
for (int j = 1; j <= n; j++) { // 遍历背包
if (j - i * i >= 0) {
dp[j] = min(dp[j - i * i] + 1, dp[j]);
}
}
}
return dp[n];
}
};
```
## 总结
本周的主题其实就是背包问题中的遍历顺序!
我这里做一下总结:
求组合数:[动态规划518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)
求排列数:[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)、[动态规划70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)
求最小数:[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)、[动态规划279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)
此时我们就已经把完全背包的遍历顺序研究的透透的了!