LeetCode 123. Best Time to Buy and Sell Stock III

29 篇文章 0 订阅
13 篇文章 0 订阅

Problem

题目链接

Notes

Best Time to Buy and Sell Stock系列第一题要求的最多一次交易的最大收益,Best Time to Buy and Sell Stock系列第二题要求的无限次交易的最大收益。此题为该系列第三题,要求了交易次数为两次。其实,要求为一次或无限次,求解很简单。但交易次数为k次(k为>1的常数),事情就变得困难起来,因为有了次数限制,我们必须面对很多子问题作出取舍,所以需要通过dp来求解。
自己一开始想的dp[i][j],i、j是表示两个售出的日子,搞得十分复杂也没搞出来。
下面的dp思路是借鉴他人内容
我们把dp[k][j]看作第k次交易在第j天时的最大收益。
那么我们首先得到初始化条件,即dp[0][0~n]=0,dp[k][0]=0,很好理解,第0次交易肯定无法获得利润吧,在第0天买卖肯定无法获得利润吧~之所以要维护第0次交易这个无实际意义的内容,是为了使后面的代码编写更方便。
然后确定递归方程dp[k][j]=max(dp[k][j-1],prices[j]-prices[i]+dp[k-1][i-1]),解释一下,如果第j天不买不卖,那么当天的利润肯定和前一天一样即dp[k][j-1],如果卖了,那么卖出价就是j这天的价格,买入价肯定就是j之前的某一天i的价格啦,除了这次获利,还要加上之前k-1次获利即dp[k-1][i-1] (这里可以发现我们初始化了dp[0][]的好处了,就是本来计算k=1时是要特判的,因为不存在k-1=0交易,但通过初始化dp[0]这一维都为0,就可以写成一种情况而不用特判了)。
这就是第j天可能发生的两种情况了,两者取最大就可以了,返回dp[2][n-1]就是最后的解。
version1的代码在“找买入价”这一步时,每次都需要在0~卖出日里把买入日找出来。这份代码是超时的。
version2优化了上述那一层循环。version1中可以明显发现,在version1中随着i增大,每次j都从头开始找,导致做了很多重复的工作,我们可以边更新dp数组然后边找0~i的最优买入日。
version3开始,我们着手与优化空间复杂度。version3所做的工作就是将k循环和i循环交换了次序。由于次序的交换,每个i都会对所有k值进行更新,相当于对于k进行类似并行的更新,所以我们需要记录下每个k对应的最小值。
version4。由version3可以明显看到,dp数组的第i次更新,只依赖于i-1这一项,那么我们就不需要记录所有的dp中间过程了。

Codes

version 1 //超时

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n<=1) return 0;
        int dp[3][n];
        for(int i=0;i<n;++i) dp[0][i]=0;
        for(int k=1;k<=2;++k)
        {
            dp[k][0]=0;
            for(int i=1;i<n;++i)
            {
                int tempmin=prices[0];
                // J循环为了找出在哪一天买入
                for(int j=1;j<i;++j)
                {
               //如果没有初始化dp[0]这一维为0,k=1时要特判,因不存在k=0这次交易
                    tempmin=min(tempmin,prices[j]-dp[k-1][j-1]);
                }
                dp[k][i]=max(dp[k][i-1],prices[i]-tempmin);
            }
        }
        return dp[2][n-1];
    }
};

version 2 //优化,少了一重循环

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n<=1) return 0;
        int dp[3][n];
        for(int i=0;i<n;++i) dp[0][i]=0;
        for(int k=1;k<=2;++k)
        {
            dp[k][0]=0;
            int tempmin=prices[0];
            for(int i=1;i<n;++i)
            {
                dp[k][i]=max(dp[k][i-1],prices[i]-tempmin);
                tempmin=min(tempmin,prices[i]-dp[k-1][i-1]);
            }
        }
        return dp[2][n-1];
    }
};

version3

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n<=1) return 0;
        int dp[3][n];
        int m[3];
        for(int i=0;i<n;++i) dp[0][i]=0;
        for(int i=1;i<3;++i) dp[i][0]=0;
        for(int i=1;i<3;++i) m[i]=prices[0];
        for(int i=1;i<n;++i)
        {
            for(int k=1;k<3;++k)
            {
                dp[k][i]=max(dp[k][i-1],prices[i]-m[k]);
                m[k]=min(m[k],prices[i]-dp[k-1][i-1]);
            }
        }
        return dp[2][n-1];
    }
};

version4

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n<=1) return 0;
        int dp[3];
        int m[3];
        for(int i=0;i<3;++i) dp[i]=0;
        for(int i=1;i<3;++i) m[i]=prices[0];
        for(int i=1;i<n;++i)
        {
            for(int k=1;k<3;++k)
            {
                dp[k]=max(dp[k],prices[i]-m[k]);
                m[k]=min(m[k],prices[i]-dp[k-1]);
            }
        }
        return dp[2];
    }
};

Results

version2:
在这里插入图片描述

version4:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值