123.买卖股票的最佳时机III ★
文档讲解 : 代码随想录 - 123.买卖股票的最佳时机III
状态:再次回顾。(★:需要多次回顾并重点回顾。)
动态规划五部曲:
-
确定dp数组(dp table)以及下标的含义
一天一共就有五个状态:- 没有操作 (其实我们也可以不设置这个状态)
- 第一次持有股票
- 第一次不持有股票
- 第二次持有股票
- 第二次不持有股票
dp[i][j]
中i
表示第i天,j
为[0 - 4]
五个状态,dp[i][j]
表示第i
天状态j
所剩最大现金。
-
确定递推公式
达到dp[i][1]
状态,有两个具体操作:- 操作一:第
i
天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
- 操作二:第
i
天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]
选最大的,所以dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);
同理
dp[i][2]
也有两个操作:- 操作一:第
i
天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
- 操作二:第
i
天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]
所以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]);
- 操作一:第
-
dp数组如何初始化
dp[0][0] = 0; dp[0][1] = -price[0]; dp[0][2] = 0; dp[0][3] = -price[0]; dp[0][4] = 0;
-
确定遍历顺序
从递推公式可以看出dp[i]
都是由dp[i - 1]
推导出来的,那么一定是从前到后遍历! -
举例推导dp数组:
以输入[1,2,3,4,5]
为例:
本题代码:
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];
}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n × 5 ) O(n × 5) O(n×5)
188.买卖股票的最佳时机IV ★
文档讲解 : 代码随想录 - 买卖股票的最佳时机IV
状态:再次回顾。(★:需要多次回顾并重点回顾。)
动态规划五部曲:
-
确定dp数组(dp table)以及下标的含义
使用二维数组dp[i][j]
:第i
天的状态为j
,所剩下的最大现金是dp[i][j]
:
j的状态表示为:- 0 表示不操作
- 1 第一次买入
- 2 第一次卖出
- 3 第二次买入
- 4 第二次卖出
- …
至多有k
笔交易,那么j
的范围就定义为2 * k + 1
二维dp数组的C++定义为:vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
-
确定递推公式
本题和上一题动态规划:123.买卖股票的最佳时机III最大的区别就是这里要类比j
为奇数是买,偶数是卖的状态。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]); }
-
dp数组如何初始化
在初始化的地方同样要类比j
为偶数是卖、奇数是买的状态。for (int j = 1; j < 2 * k; j += 2) { dp[0][j] = -prices[0]; }
-
确定遍历顺序
从递推公式可以看出dp[i]
都是由dp[i - 1]
推导出来的,那么一定是从前到后遍历! -
举例推导dp数组:
以输入[1,2,3,4,5]
,k=2
为例:
本题代码:
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
for (int j = 1; j < 2 * k; j += 2) {
dp[0][j] = -prices[0];
}
for (int i = 1;i < prices.size(); i++) {
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]);
}
}
return dp[prices.size() - 1][2 * k];
}
};
- 时间复杂度:
O
(
n
∗
k
)
O(n * k)
O(n∗k),其中
n
为prices
的长度 - 空间复杂度: O ( n ∗ k ) O(n * k) O(n∗k)