最简单版本:只有一次交易机会,某天买某天卖:买卖股票1
//对于每一天i,只要知道前面最小的是哪天,就可以知道以i结尾的最大利润
int maxProfit(vector<int>& prices) {
int buyDay = 0;
int maxProfit = 0;
for(int i=1;i<prices.size();i++){
if(prices[i]<prices[buyDay])
buyDay = i;
int tempProfit = prices[i] - prices[buyDay];
maxProfit = max(maxProfit,tempProfit);
}
return maxProfit;
}
不限次数的交易: 买卖股票2
你可以尽可能地完成更多的交易(多次买卖一支股票)
//连续的持有也可以视作是买了再卖出,反正次数不限
int maxProfit(vector<int>& prices) {
int profitSum = 0;
for(int i=1;i<prices.size();i++){
if(prices[i] < prices[i-1] ){
continue;
}
profitSum += prices[i] - prices[i-1]; //只要有利润就买卖
}
return profitSum;
}
第二种写法,通解法,状态转移。
//dp[i][1]第i天持有股票,dp[i][0]第i天不持有股票
int maxProfit(vector<int>& prices) {
int dp[100005][2];
dp[0][0] = 0;
dp[0][1] = -prices[0]; //初始如果买入,到第0天为止的利润是负数哦
for(int i=1;i<prices.size();i++){
dp[i][0] = max(dp[i-1][1] + prices[i], dp[i-1][0]); //不持有=前一天不持有保持 or 前一天持有+今天卖出
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]); //持有=前一天持有保持 or 前一天没有+今天买入
}
return dp[prices.size()-1][0];
}
含冷冻期的情况
我本来的想法是,按照上面的2的写法,只需要额外判断买入的时候 d p [ i ] = d p [ i − 1 ] [ 0 ] − p r i c e dp[i]=dp[i-1][0] - price dp[i]=dp[i−1][0]−price 前一天i-1是否是卖出的,判断的逻辑就是看dp[i-2][0]是否和[i-1]一样。但这样做不可的原因是:冷冻期会对后面造成影响,因此无法简单的肯定dp[i][0]在 i 天的最优值,你看似在当前最优了,但你的冷冻对也许耽误了后面的大单子。因此冷冻期也应该是状态的一部分。
[0]的不持有情况分下
- 今天卖出 —— 对后面一天有影响
- 昨天卖出,或者再之前卖出,顺延之前——对后面无影响
为了区分,我们让今天卖出还是 [0], 非今天卖出的记为[2]
理一下状态转移:
dp[i][1] = dp[i-1][1]
o
r
or
or dp[i-1][2] - price
dp[i][0] = dp[i-1][1] + price
dp[i][2] = dp[i-1][0]
o
r
or
or dp[i-1][2]
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int> > dp(n,vector<int>(3,0)); //0表示今天卖(后面不准买),1表示现在持有,2表示昨天及之前卖
dp[0][1] = -prices[0];
for(int i=1; i<n; i++){
dp[i][0] = dp[i-1][1] + prices[i];
dp[i][1] = max(dp[i-1][1],dp[i-1][2]-prices[i]);
dp[i][2] = max(dp[i-1][0],dp[i-1][2]);
}
return max(dp[n-1][0],dp[n-1][2]);
}
我们发现这种题型其实状态转移不难想,重要的是分析有几种状态。
股票问题通解:给你股票每天的价格,限定你只能进行K次交易(buy and sell), 求最大的收入。
求第n天交易k次的收入,一般做动态规划的习惯思路会想到:dp[n][k]
,所以会想到用 dp[i][j] 表示第i天为止进行 j 次交易的利润。
我们思考每天的选择,只有三种:sell, buy, keep
因为sell, buy是有前序关系的,必须前面有buy才能sell, 因此我们的状态必须加上一维表示是否前面buy过。
- dp[][][0]表示没buy呢
- dp[][][1]表示已经buy过了
状态转换
(buy这个行为表示进入了下一次交易j+1)
由此推导我们的公式:dp的值表示利润
d p [ i ] [ j ] [ 0 ] = m a x ( d p [ i − 1 ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 1 ] + p r i c e s [ i ] ) dp[i][j][0] = max( dp[i-1][j][0], dp[i-1][j][1] + prices[i]) dp[i][j][0]=max(dp[i−1][j][0],dp[i−1][j][1]+prices[i])
d p [ i ] [ j ] [ 1 ] = m a x ( d p [ i − 1 ] [ j ] [ 1 ] , d p [ i − 1 ] [ j − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i]) dp[i][j][1]=max(dp[i−1][j][1],dp[i−1][j−1][0]−prices[i])
注意初始状态,也就是转换中i-1可能不存在,j-1可能不存在的时候。
i == 0: dp[0][][1] = -prices[i] //表示初始买入
j == 0: dp[][0][1] = -inf //表示不可能
int maxProfit(vector<int>& prices) {
int dp[100005][3][2] = {0};
int inf = 1000000000;
for(int i=0;i<prices.size();i++){
for(int k=0;k<=2;k++){
//中间涉及i-1,k-1不存在的情况处理
if( k==0 ){
dp[i][k][1] = -inf;
continue;
}
if(i==0){
dp[i][k][1] = -prices[i];
continue;
}
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]);
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]);
}
}
return dp[prices.size()-1][2][0];
}