121. 买卖股票的最佳时机
(1)题目描述:

,
(2)解题思路:
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
if (len == 0) return 0;
vector<vector<int>> dp(len, vector<int>(2));
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++) {
dp[i][0] = max(dp[i - 1][0], -prices[i]);
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
}
return dp[len - 1][1];
}
};
(3)总结:
1.确定dp数组(dp table)以及下标的含义
dpi0 表示第i天持有股票所得最多现金 ,这里可能有同学疑惑,本题中只能买卖一次,持有股票之后哪还有现金呢?
其实一开始现金是0,那么加入第i天买入股票现金就是 -pricesi, 这是一个负数。
dpi1 表示第i天不持有股票所得最多现金
注意这里说的是"持有","持有"不代表就是当天"买入"!也有可能是昨天就买入了,今天保持持有的状态
2.确定递推公式
如果第i天持有股票即dpi0, 那么可以由两个状态推出来
第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dpi - 10
第i天买入股票,所得现金就是买入今天的股票后所得现金即:-pricesi
那么dpi0应该选所得现金最大的,所以dpi0 = max(dpi - 10, -pricesi);
如果第i天不持有股票即dpi1, 也可以由两个状态推出来
第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dpi - 11
第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:pricesi + dpi - 10
同样dpi1取最大的,dpi1 = max(dpi - 11, pricesi + dpi - 10);
3.dp数组如何初始化
由递推公式 dpi0 = max(dpi - 10, -pricesi); 和 dpi1 = max(dpi - 11, pricesi + dpi - 10);可以看出
其基础都是要从dp00和dp01推导出来。
那么dp00表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp00 -= prices0;
dp01表示第0天不持有股票,不持有股票那么现金就是0,所以dp01 = 0;
4.确定遍历顺序
从递推公式可以看出dpi都是由dpi - 1推导出来的,那么一定是从前向后遍历。
122.买卖股票的最佳时机II
(1)题目描述:


(2)解题思路:
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
vector<vector<int>> dp(len, vector<int>(2, 0));
dp[0][0] -= prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 注意这里是和121. 买卖股票的最佳时机唯一不同的地方。
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
}
return dp[len - 1][1];
}
};
(3)总结:
1.本题与上一题的区别在于可以买卖多次,那在第i天买卖股票的时候,手头的现金可能已经不是0了
2.其余步骤都与上期一样
123.买卖股票的最佳时机III
(1)题目描述:


(2)解题思路:
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = dp[i - 1][0];
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
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]);
}
return dp[prices.size() - 1][4];
}
};
(3)总结:
1.注:最多可以完成两笔交易(可以当天买卖)
1.确定dp数组以及下标的含义
一天一共就有五个状态,
没有操作 (其实我们也可以不设置这个状态)
第一次持有股票
第一次不持有股票
第二次持有股票
第二次不持有股票
dpij中 i表示第i天,j为 0 - 4 五个状态,dpij表示第i天状态j所剩最大现金。
需要注意:dpi1,表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票
例如 dpi1 ,并不是说 第i天一定买入股票,有可能 第 i-1天 就买入了,那么 dpi1 延续买入股票的这个状态。
2.确定递推公式
达到dpi1状态,有两个具体操作:
操作一:第i天买入股票了,那么dpi1 = dpi-10 - pricesi
操作二:第i天没有操作,而是沿用前一天买入的状态,即:dpi1 = dpi - 11
那么dpi1究竟选 dpi-10 - pricesi,还是dpi - 11呢?
一定是选最大的,所以 dpi1 = max(dpi-10 - pricesi, dpi - 11);
同理dpi2也有两个操作:
操作一:第i天卖出股票了,那么dpi2 = dpi - 11 + pricesi
操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dpi2 = dpi - 12
所以dpi2 = max(dpi - 11 + pricesi, dpi - 12)
同理可推出剩下状态部分:
dpi3 = max(dpi - 13, dpi - 12 - pricesi);
dpi4 = max(dpi - 14, dpi - 13 + pricesi);
3.dp数组如何初始化
第0天没有操作,这个最容易想到,就是0,即:dp00 = 0;
第0天做第一次买入的操作,dp01 = -prices0;
第0天做第一次卖出的操作,这个初始值应该是多少呢?
此时还没有买入,怎么就卖出呢? 其实大家可以理解当天买入,当天卖出,所以dp02 = 0;
第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
所以第二次买入操作,初始化为:dp03 = -prices0;
同理第二次卖出初始化dp04 = 0;
4.确定遍历顺序
从递归公式其实已经可以看出,一定是从前向后遍历,因为dpi,依靠dpi - 1的数值。