309.买卖股票最佳时机含冷冻期
-
本题是加了一个冷冻期,状态就多了,有点难度,大家要把各个状态分清,思路才能清晰
-
股票系列难度:冷冻期>K次交易>两次交易>手续费,重点是理解状态转移。
-
K次交易是仔细分析状态转移;冷冻期是分清楚每个状态,对普通状态进行拆分;两次交易是K次交易简化版,也是要分清楚交易状态。手续费只是在无限次交易基础上,加上了每笔交易结束之后要付钱。
给定一个整数数组prices
,其中第 prices[i]
表示第 i
天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例 2:
输入: prices = [1]
输出: 0
提示:
1 <= prices.length <= 5000
0 <= prices[i] <= 1000
思路
本题和Ⅱ有点像,也是无限次买卖股票,但是加上了冷冻期。针对冷冻期,我们也需要列出来每一天的状态,分析每一天状态的关系。
注意,本题的冷冻期是仅仅当天卖出之后才会有第二天冷冻期,冷冻期过了才能重新买入,因此冷冻期一定是不持有股票的状态!
之前的题目里,不持有股票的状态分为之前就不持有和当天刚刚卖出。但是本题,这两种状态需要做拆分,因为当天刚刚卖出的情况会引起冷冻期!
也就是说,当天卖出股票,跟随的下一天就是冷冻期,也就是下一个状态是冷冻期!
DP数组含义
dp[i][0]
持有股票的状态dp[i][1]
不持有股票,之前就不持有的状态(注意拆分之后,之前就不持有的状态就不能加上刚刚卖出了,但是这种情况又包含了过了冷冻期的情况)dp[i][2]
不持有股票,当天刚刚卖出的状态dp[i][3]
冷冻期状态(持续一天,第i天是冷冻期)
递推公式
dp[i][0]
:持有股票状态,前一天就持有或者今天刚刚买入,今天刚刚买入可能是之前不持有买入,或者是过了冷冻期买入,也就是可能从两个状态转移过来!
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i],dp[i-1][3]-prices[i]);
dp[i][1]
:之前就不持有状态,已经拆分掉了今天刚刚卖的情况(在状态2),那么只可能是本来就不持有,或者是冷冻期刚过,也就是说只可能从状态1和状态3转移过来!
dp[i][1]=max(dp[i-1][1],dp[i-1][3]);
dp[i][2]
:不持有股票,当天刚刚卖出状态,之前一定是有的,从状态0转移过来
dp[i][2]=dp[i-1][0]+prices[i];
dp[i][3]
:冷冻期状态,一定是前一天刚刚卖出,从状态2转移过来,冷冻期不能操作
dp[i][3]=dp[i-1][2];
初始化
对i=0的情况进行初始化,也就是下标第i天的股票情况。
dp[0][0]=-prices[0];
dp[0][1]=0;
dp[0][2]=0;//刚刚卖了
dp[0][3]=0;//第一天买了+卖了,直接进入冷冻期
完整版
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()==1) return 0;
vector<vector<int>>dp(prices.size(),vector<int>(4,0));
//0这里初始化一定是-prices[0]而不是-prices[i],不能手误!
dp[0][0]=-prices[0];//0:持有
dp[0][1]=0;//1:保持不持有,可能是之前就不持有
dp[0][2]=0;//2:当天卖了,引发第二天冷冻期
dp[0][3]=0;//3:冷冻期
for(int i=1;i<prices.size();i++){
//0:持有股票状态,可能第一次买,也可能昨天是冷冻期
dp[i][0]=max(dp[i-1][0],max(dp[i-1][1]-prices[i],dp[i-1][3]-prices[i]));
//1:不持有
dp[i][1]=max(dp[i-1][1],dp[i-1][3]);
dp[i][2]=dp[i-1][0]+prices[i];//前一天只有可能是持有
dp[i][3]=dp[i-1][2];
}
//在对应的dp[size()-1]的一维数组里面找最大值
return *max_element(dp[prices.size()-1].begin(),dp[prices.size()-1].end());
}
};
注意Max的用法
遇到三个数字的比较的时候,必须要进行max的嵌套!
总结
冷冻期这道题,关键是要想到拆分之前题目中的,不持有股票这个状态下的两种情况,即为本来就不持有和当天刚刚卖出。因为当天刚刚卖出这种情况,会导致冷冻期。因此,当天刚刚卖出应该单独拆分为一个状态,由该状态导致的冷冻期应该拆分为另一个状态。
核心点还是理解状态转移的思想。
714.买卖股票最佳时机含手续费
给定一个整数数组 prices
,其中 prices[i]
表示第 i
天的股票价格 ;整数 fee
代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
示例 2:
输入:prices = [1,3,7,5,10,3], fee = 3
输出:6
提示:
1 <= prices.length <= 5 * 10^4
1 <= prices[i] < 5 * 10^4
0 <= fee < 5 * 10^4
思路
和冷冻期一样,本题同样和Ⅱ类似,都是无限次买卖股票。本题区别在于每笔交易都需要支付一笔手续费,支付手续费的时间可以在买入支付,也可以在卖出支付,每笔交易只需要支付一次。
本题并不需要额外划分状态,只需要持有股票和不持有股票两种即可。
DP数组含义
dp[i][0]
持有股票的状态,由本来就持有状态和当天刚刚买入状态得到。
dp[i][1]
不持有股票的状态,由本来就不持有状态和当天刚刚卖出状态得到(卖出减去手续费)
递推公式
持有股票状态:
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]-fee);
初始化
本题的初始化也是初始化dp[0][0]
和dp[0][1]
,因为只有0和1两种状态。
vector<vector<int>>dp(prices.size(),vector<int>(2,0));
dp[0][0]=-prices[0];//只有持有状态需要变化
完整版
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
vector<vector<int>>dp(prices.size(),vector<int>(2,0));
dp[0][0]=-prices[0];
for(int i=1;i<prices.size();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]-fee);
}
return dp[prices.size()-1][1];
}
};
总结
本题和买卖股票Ⅱ很像,多了手续费,手续费是每次交易只需要一次,因此交易结束的时候上交。
股票系列总结:
- 股票系列难度:冷冻期>K次交易>两次交易>手续费,重点是理解状态转移。
- K次交易是仔细分析状态转移;冷冻期是分清楚每个状态,对普通状态进行拆分;两次交易是K次交易简化版,也是要分清楚交易状态。手续费只是在无限次交易基础上,加上了每笔交易结束之后要付钱。