文章目录
- 1. 买股票的最佳时机I
- 2. 买股票的最佳时机II
- 3. 最佳买卖股票时机
- 4. 买股票的最佳时机III
- 5. 买股票的最佳时机IV
1. 买股票的最佳时机I
dp数组含义,本题两个状态:持有股票、不持有股票
- dp[i][1] :表示第i天不持有股票所得最多现金
- dp[i][0] :表示第i天持有股票所得最多现金
如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来
- 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
- 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i],那么dp[i][0]应该选所得现金最大的,所以
dp[i][0] = max(dp[i - 1][0], -prices[i]);
如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来
- 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
- 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
从递推公式可以看出,dp[i]只是依赖于dp[i - 1]的状态。所以我们只需要申请2*2的空间就可以啦。
[7,1,5,3,6,4]为例,dp数组状态如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int dp[2][2] = {0};
dp[0][0] -= prices[0];
dp[0][1] = 0;
for(int i = 1; i < prices.size(); ++i){
dp[1][0] = max(-prices[i], dp[0][0]);
dp[1][1] = max(prices[i] + dp[0][0], dp[0][1]);
dp[0][0] = dp[1][0];
dp[0][1] = dp[1][1];
}
return dp[1][1];
}
};
2. 买股票的最佳时机II
- 方法一:动态规划
和上一题的唯一区别就是股票可以买卖多次,所以买入股票的时候,可能会有之前买卖的利润,所以dp[1][0] = max(dp[0][0], dp[0][1] - prices[i]);
以[8,9,2,5,4,7,1]为例,dp数组为
class Solution {
public:
int maxProfit(vector<int>& prices) {
int dp[2][2];
dp[0][0] = -prices[0];
dp[0][1] = 0;
for(int i = 1; i < prices.size(); ++i){
dp[1][0] = max(dp[0][0], dp[0][1] - prices[i]);
dp[1][1] = max(dp[0][1], dp[0][0] + prices[i]);
dp[0][0] = dp[1][0];
dp[0][1] = dp[1][1];
}
return dp[1][1];
}
};
- 方法二:直接把价格上升的部分累加
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0;
for(int i = 1; i < prices.size(); ++i){
if(prices[i] > prices[i - 1]) res += prices[i] - prices[i - 1];
}
return res;
}
};
3. 最佳买卖股票时机
本题就是在 买卖股票的最佳时机 II 的基础上加上了冷冻期,我们将状态分为三种:持有股票、不持有股票处于冷冻期、不持有股票不处于冷冻期。dp数组含义:
- dp[i][0]:持有股票,能获得的最大收益
- dp[i][1]:不持有股票,不在冷冻期,能获得的最大收益
- dp[i][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]
以[1,2,3,0,2]为例,dp数组为:
dp[i]只是依赖于dp[i - 1]的状态。所以我们只需要申请2*3的空间就可以啦。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int dp[2][3] = {0};
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
for(int i = 1; i < prices.size(); ++i){
dp[1][0] = max(dp[0][0], dp[0][1] - prices[i]);
dp[1][1] = max(dp[0][1], dp[0][2]);
dp[1][2] = dp[0][0] + prices[i];
dp[0][0] = dp[1][0];
dp[0][1] = dp[1][1];
dp[0][2] = dp[1][2];
}
return max(dp[1][1], dp[1][2]);
}
};
4. 买股票的最佳时机III
一天一共就有五个状态:
- 未操作过
- 处于第一次持有股票的状态
- 处于第一次持有股票、又卖出的状态
- 处于第二次持有股票的状态
- 处于第二次不持有股票、又卖出的状态
dp数组含义
dp[i][j]:表示第i天,处于状态j时,所能获得的最大收益
- dp[i][0]:没买卖过股票,所能获得的最大收益
- dp[i][1]:处于第一次持有股票的状态,所能获得的最大收益
- dp[i][2]:处于第一次持有股票、又卖出的状态,所能获得的最大收益
- dp[i][3]:处于第二次持有股票的状态,所能获得的最大收益
- dp[i][4]:处于第二次不持有股票、又卖出的状态,所能获得的最大收益
递推公式:
- dp[i][0]:没买卖过股票,所能获得的最大收益保持为0
- dp[i][1]:第一次处于持有股票状态的最大收益
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
- dp[i][2]:第一次处于持有股票、又卖出状态的最大收益
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
- dp[i][3]:第二次处于持有股票状态的最大收益
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i])
- dp[i][4]:第二次处于持有股票、又卖出状态的最大收益
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
初始化
- dp[0][0]:第0天没操作,收益为0
- dp[0][1]:第0天买入,收益为-prices[0]
- dp[0][2]:第0天买入后卖出,收益为0
- dp[0][3]:第0天买入后卖出,再买入,收益为-prices[0]
- dp[0][4]:第0天买入后卖出,再买入卖出,收益为0
以[8,9,3,5,1,3]为例,dp数组为
dp[i]只是依赖于dp[i - 1]的状态。所以我们只需要申请2*5的空间就可以啦
class Solution {
public:
int maxProfit(vector<int>& prices) {
// write code here
int dp[2][5] = {0};
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
for(int i = 1; i < prices.size(); ++i){
dp[1][1] = max(dp[0][1], dp[0][0] - prices[i]);
dp[1][2] = max(dp[0][2], dp[0][1] + prices[i]);
dp[1][3] = max(dp[0][3], dp[0][2] - prices[i]);
dp[1][4] = max(dp[0][4], dp[0][3] + prices[i]);
dp[0][1] = dp[1][1];
dp[0][2] = dp[1][2];
dp[0][3] = dp[1][3];
dp[0][4] = dp[1][4];
}
return dp[1][4];
}
};
5. 买股票的最佳时机IV
上题中,最多两笔交易时,有5个状态;本题最多k笔交易,会有2*k+1个状态,因为完成k笔交易,需要k次买入和k次卖出,再加上不做任何操作的状态,总共有2*k+1个状态。
使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]
j的状态表示为:
- 0 表示不操作
- 1 第一次买入
- 2 第一次卖出
- 3 第二次买入
- 4 第二次卖出
- …
除了0以外,偶数就是卖出,奇数就是买入。
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(n, vector<int>(2 * k + 1, 0));
for(int i = 1; i < 2 * k + 1; i += 2){
dp[0][i] = -prices[0];
}
for(int i = 1; i < n; ++i){
for(int j = 1; j < 2 * k + 1; ++j){
if(j & 1) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]);
}
}
return dp[n - 1][2 * k];
}
};
压缩维度
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(2, vector<int>(2 * k + 1, 0));
for(int i = 1; i < 2 * k + 1; i += 2){
dp[0][i] = -prices[0];
}
for(int i = 1; i < n; ++i){
for(int j = 1; j < 2 * k + 1; ++j){
if(j & 1) dp[1][j] = max(dp[0][j], dp[0][j - 1] - prices[i]);
else dp[1][j] = max(dp[0][j], dp[0][j - 1] + prices[i]);
dp[0][j] = dp[1][j];
}
}
return dp[1][2 * k];
}
};