股票买卖——状态如何表示与转换

最简单版本:只有一次交易机会,某天买某天卖:买卖股票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[i1][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[i1][j][0],dp[i1][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[i1][j][1],dp[i1][j1][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];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值