LeetCode 123.买卖股票的最佳时机 III

LeetCode 123.买卖股票的最佳时机 III

执行用时 :4 ms, 在所有 Java 提交中击败了70.42%的用户

序列型动态规划
首先对于股票交易要有几个认识:

图片引用LeetCode

对于上图,若此图为股票的价格走势,那么要求最大利润,直观来说就是图中的 p e e k j − v a l l e y i peek_j-valley_i peekjvalleyi,但是从另一种角度,第2天和第5天之间是涨是跌,都与最后最大利润的值无关,另外,最大利润的求解不仅可以直观地用最大值减去最小值,若从第2天之后,每天的利润(每天的价格减去前一天的价格)相加,到第五天也会得到所求。

进入正题:

确定状态:

截止到第i天(不含第i天),在阶段j的最大获利为f[i][j]
将整个过程分为五个阶段:
1. 第一次未持有股票(第一次买之前):
要么是初始状态,要么一直没买,没有利润。
2. 第一次持有股票:
若昨天有股票,那么今天保持状态, 截止到今天的利润等于将截止到昨天这个阶段的利润加上 今天这个阶段的利润,即f[i][j]=f[i-1][j]+prices[i]-prices[i-1]若昨天没有股票,那么就是今天买入的, 截止到今天的利润等于昨天前一阶段的利润,即f[i][j]=f[i-1][j-1]
3. 第二次未持有股票(第一次卖之后,第二次买之前): 若昨天就没有股票,那么今天保持状态也没有,利润等于截止到昨天这个阶段的,即f[i][j]=f[i-1][j].若是今天才卖的,截止到今天的利润等于截止到昨天前一阶段的利润加上今天所得的利润,即f[i][j]=f[i-1][j-1]+prices[i]-prices[i-1]
4. 第二次持有股票:与第一次持有股票同理
5. 第三次未持有股票(第二次卖之后):与第二次未持有股票同理

转移方程:

1. 阶段1、3、5:未持有股票
f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] ( 昨 天 未 持 有 股 票 ) , f [ i − 1 ] [ j − 1 ] + p r i c e s [ i ] − p r i c e s [ i − 1 ] ( 今 天 卖 出 ) } f[i][j]=max\{f[i-1][j](昨天未持有股票),f[i-1][j-1]+prices[i]-prices[i-1](今天卖出)\} f[i][j]=max{f[i1][j](),f[i1][j1]+prices[i]prices[i1]()}
2. 阶段2、4:持有股票
f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] + p r i c e s [ i ] − p r i c e s [ i − 1 ] ( 昨 天 持 有 股 票 ) , f [ i − 1 ] [ j − 1 ] ( 今 天 买 入 ) } f[i][j]=max\{f[i-1][j]+prices[i]-prices[i-1](昨天持有股票),f[i-1][j-1](今天买入)\} f[i][j]=max{f[i1][j]+prices[i]prices[i1](),f[i1][j1]()}

初始条件和边界情况:
注意实现时prices数组索引从0开始;f数组索引从1开始,0作为初始条件。所以以上所列方程在实现时prices数组中的索引全部-1 刚开始在阶段1: f [ 0 ] [ 1 ] = 0 , f [ 0 ] [ 2 ] = f [ 0 ] [ 3 ] = f [ 0 ] [ 4 ] = f [ 0 ] [ 5 ] = − ∞ f[0][1]=0,f[0][2]=f[0][3]=f[0][4]=f[0][5]=-\infty f[0][1]=0f[0][2]=f[0][3]=f[0][4]=f[0][5]=数组索引别越界,若j-1<1或i-2<0,对应项不参与max运算。
最后答案就是 m a x { f [ n ] [ 1 ] , f [ n ] [ 3 ] , f [ n ] [ 5 ] } max\{f[n][1],f[n][3],f[n][5]\} max{f[n][1],f[n][3],f[n][5]}

class Solution {
    public int maxProfit(int[] prices) {
        int n=prices.length;
        if(n==0)
            return 0;
        int [][]f=new int[n+1][5+1];
        f[0][1]=0;
        f[0][2]=f[0][3]=f[0][4]=f[0][5]=Integer.MIN_VALUE;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=5;j++)
            {
                if(j==1||j==3||j==5)//未持有股票
                {
                    f[i][j]=f[i-1][j];
                    if(i>1&&j>1&&f[i-1][j-1]!=Integer.MIN_VALUE)
                     f[i][j]=Math.max(f[i-1][j],//因无活动,保持前一天的阶段
                                      prices[i-1]-prices[i-2]+f[i-1][j-1]);//因卖出股票后而未持有,计算利润
                }
                if(j==2||j==4)//持有股票
                {
                    f[i][j]=f[i-1][j-1];
                    if(i>1&&f[i-1][j]!=Integer.MIN_VALUE)
                    f[i][j]=Math.max(prices[i-1]-prices[i-2]+f[i-1][j],//因一直持有股票,计算当天利润
                                     f[i-1][j-1]);//因未持有股票,买入
                }
            }
        }
        return Math.max(f[n][1],Math.max(f[n][3],f[n][5])); 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值