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

209 lines
6.7 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.

本周的主题就是股票系列,来一起回顾一下吧
## 周一
[动态规划买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II动态规划.html)中股票可以买卖多次了!
这也是和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别(注意只有一只股票,所以再次购买前要出售掉之前的股票)
重点在于递推公式的不同。
在回顾一下dp数组的含义
* dp[i][0] 表示第i天持有股票所得现金。
* dp[i][1] 表示第i天不持有股票所得最多现金
递推公式:
```
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
```
大家可以发现本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的代码几乎一样,唯一的区别在:
```
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
```
**这正是因为本题的股票可以买卖多次!** 所以买入股票的时候可能会有之前买卖的利润即dp[i - 1][1]所以dp[i - 1][1] - prices[i]。
## 周二
[动态规划买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)中最多只能完成两笔交易。
**这意味着可以买卖一次,可以买卖两次,也可以不买卖**
1. 确定dp数组以及下标的含义
一天一共就有五个状态,
0. 没有操作
1. 第一次买入
2. 第一次卖出
3. 第二次买入
4. 第二次卖出
**dp[i][j]中 i表示第i天j为 [0 - 4] 五个状态dp[i][j]表示第i天状态j所剩最大现金**
2. 确定递推公式
需要注意dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。
```
dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);
dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2]);
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
```
3. dp数组如何初始化
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[0][4] = 0;
4. 确定遍历顺序
从递归公式其实已经可以看出一定是从前向后遍历因为dp[i]依靠dp[i - 1]的数值。
5. 举例推导dp数组
以输入[1,2,3,4,5]为例
![123.买卖股票的最佳时机III](https://code-thinking-1253855093.file.myqcloud.com/pics/20201228181724295.png)
可以看到红色框为最后两次卖出的状态。
现在最大的时候一定是卖出的状态,而两次卖出的状态现金最大一定是最后一次卖出。
所以最终最大利润是dp[4][4]
## 周三
[动态规划买卖股票的最佳时机IV](https://programmercarl.com/0188.买卖股票的最佳时机IV.html)最多可以完成 k 笔交易。
相对于上一道[动态规划123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)本题需要通过前两次的交易来类比前k次的交易
1. 确定dp数组以及下标的含义
使用二维数组 dp[i][j] 第i天的状态为j所剩下的最大现金是dp[i][j]
j的状态表示为
* 0 表示不操作
* 1 第一次买入
* 2 第一次卖出
* 3 第二次买入
* 4 第二次卖出
* .....
**除了0以外偶数就是卖出奇数就是买入**
2. 确定递推公式
还要强调一下dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。
```CPP
for (int j = 0; j < 2 * k - 1; j += 2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
```
**本题和[动态规划123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)最大的区别就是这里要类比j为奇数是买偶数是卖的状态**
3. dp数组如何初始化
**dp[0][j]当j为奇数的时候都初始化为 -prices[0]**
代码如下:
```CPP
for (int j = 1; j < 2 * k; j += 2) {
dp[0][j] = -prices[0];
}
```
**在初始化的地方同样要类比j为奇数是买、偶数是卖的状态**
4. 确定遍历顺序
从递归公式其实已经可以看出一定是从前向后遍历因为dp[i]依靠dp[i - 1]的数值。
5. 举例推导dp数组
以输入[1,2,3,4,5]k=2为例。
![188.买卖股票的最佳时机IV](https://code-thinking-1253855093.file.myqcloud.com/pics/20201229100358221-20230310133805763.png)
最后一次卖出一定是利润最大的dp[prices.size() - 1][2 * k]即红色部分就是最后求解。
## 周四
[动态规划:最佳买卖股票时机含冷冻期](https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html)尽可能地完成更多的交易多次买卖一支股票但有冷冻期冷冻期为1天
相对于[动态规划122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II动态规划.html),本题加上了一个冷冻期
**本题则需要第三个状态:不持有股票(冷冻期)的最多现金**
动规五部曲,分析如下:
1. 确定dp数组以及下标的含义
**dp[i][j]第i天状态为j所剩的最多现金为dp[i][j]**
j的状态为
* 0持有股票后的最多现金
* 1不持有股票能购买的最多现金
* 2不持有股票冷冻期的最多现金
2. 确定递推公式
```
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
dp[i][2] = dp[i - 1][0] + prices[i];
```
3. dp数组如何初始化
可以统一都初始为0了。
代码如下:
```CPP
vector<vector<int>> dp(n, vector<int>(3, 0));
```
**初始化其实很有讲究很多同学可能是稀里糊涂的全都初始化0反正就可以通过但没有想清楚为什么都初始化为0**
4. 确定遍历顺序
从递归公式上可以看出dp[i] 依赖于 dp[i-1],所以是从前向后遍历。
5. 举例推导dp数组
以 [1,2,3,0,2] 为例dp数组如下
![309.最佳买卖股票时机含冷冻期](https://code-thinking-1253855093.file.myqcloud.com/pics/20201229163725348.png)
最后两个状态 不持有股票(能购买) 和 不持有股票(冷冻期)都有可能最后结果,取最大的。
## 总结
下周还会有一篇股票系列的文章,**股票系列后面我也会单独写一篇总结,来高度概括一下,这样大家会对股票问题就有一个整体性的理解了**。
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>