本模板适用于解决买卖次数k确定
的情况
可解决力扣188、121、123题
188. 买卖股票的最佳时机 IV
思路:2 * k + 1个状态
这题是123. 买卖股票的最佳时机 III的进阶版。那么来分析一下,买卖k
次每天的状态。如下:
- 0–不操作
- 1–第一次买入
- 2–第一次卖出
- 3–第二次买入
- 4–第二次卖出
- …
2(k-1)-1=2*k-3
----第k-1
次买入2(k-1)=2*k-2
----第k-1
次卖出2*k-1
----第k
次买入2*k
----第k
次卖出
可以发现,当奇数的时候为买入,偶数的时候为卖出!
状态定义:dp[i][j]
,i
表示第i
天,j
表示第i
天的状态,j∈[0, 2k]
递推过程:
1.dp[i][1]
表示第i
天第一次买入的状态,有两种情况
- 不操作,那么跟前一天一样,即
dp[i][1]=dp[i - 1][1]
- 买入,那么会在前一天上一个状态下,即0–没有操作,花费,即
dp[i][1]=dp[i - 1][0] - prices[i]
2.dp[i][2]
表示第i
天第一次卖出的状态,有两种情况
- 不操作,那么跟前一天一样,即
dp[i][2]=dp[i - 1][2]
- 卖出,那么在前一天的上一个状态,即1–第一次买入,赚钱,即
dp[i][2]=dp[i - 1][1] + prices[i]
那么其他同理,j∈[0, 2k - 2]
,从0
开始跳,每次跳1、2个的位置。这里要注意的是j
的右边界,因为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])
;
初始化:
-
这里收集最大利润,但是股票不买不卖,最小的利润也是0,所以不会产生为负数的利润,将卖出初始化为0就可以!
-
只要买入(奇数),就需要减去prices[0]。
-
j
从1开始,到最大下标的前一个2 * k - 1
,代码如下: -
for(int j = 1; j <= 2 * k - 1; j += 2){ dp[0][j] = -prices[0]; }
整体代码如下:
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 - 1; j += 2){
dp[0][j] = -prices[0];
}
for(int i = 1; i < prices.size(); i++){ // 遍历每天价格
for(int j = 0; j <= 2 * k - 2; 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];
}
};
那么这里是不是可以总结一个模板,即无论你买卖几次(k值需要确定值),我都可以用这个模板来解?
比如,121. 买卖股票的最佳时机,只买卖一次,即k=1
,代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
return maxProfitCommon(1, prices);
}
private:
int maxProfitCommon(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 - 1; j += 2){
dp[0][j] = -prices[0];
}
for(int i = 1; i < prices.size(); i++){ // 遍历每天价格
for(int j = 0; j <= 2 * k - 2; 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];
}
};
比如,123. 买卖股票的最佳时机 III,最多买卖2次,即k=2
,代码只需要改动的仅仅是k
的值!
class Solution {
public:
int maxProfit(vector<int>& prices) {
return maxProfitCommon(2, prices);
}
private:
int maxProfitCommon(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 - 1; j += 2){
dp[0][j] = -prices[0];
}
for(int i = 1; i < prices.size(); i++){ // 遍历每天价格
for(int j = 0; j <= 2 * k - 2; 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];
}
};
参考资料:公众号代码随想录