leetcode 188. Best Time to Buy and Sell Stock IV

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Example 1:

Input: [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.

Example 2:

Input: [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
             Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.

-----------------------------------------------------------------------2018.11.18更新-------------------------------------------------------------------------

这题思路应该这么来的,sell[k][p] k表式第几次卖,p表式第几天。可以分成当天卖(sell[k][p-1])与不买两个事件,又可以把当天卖的事件再分成 之前第几天买(如果分成上一次第j天卖,则下面无法优化时间复杂度)。则得到下面O(n^3)代码。

sell[k][p]=max(sell[k][p-1],max(sell[k-1][j-1]-prices[j]+prices[p]))

class Solution {
    public int maxProfit(int k, int[] prices) {
        // int[] buy=new int[k];
        if(k==0||prices.length==0) return 0;
        if(k>prices.length/2){
            int sum=0;
            for(int i=1;i<prices.length;i++){
                if(prices[i]>prices[i-1]){
                    sum+=prices[i]-prices[i-1];                    
                }
            }
            return sum;
        }
        int[][] sell=new int[k+1][prices.length];
        //sell[p][k]=max(p,k)+max(buy[i])
        for(int i=1;i<k+1;i++){
            for(int j=1;j<prices.length;j++){
                sell[i][j]=sell[i][j-1];
                for(int m=j-1;m>=0;m--){
                    sell[i][j]=Math.max(sell[i][j],(m==0?0:sell[i-1][m-1])-prices[m]+prices[j]);//卖和买同一天情况肯定不是
                }
            }
        }
        return sell[k][prices.length-1];
    }
}

注意求sell[i][j]的时候存在大量重复每次j循环中sell[i-1][m-1]-prices[m] 重复求了,所以每次j循环中维护一个sell[i-1][m-1]-prices[m] 变量,能减少时间复杂度。

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k==0||prices.length==0) return 0;
        if(k>prices.length/2){
            int sum=0;
            for(int i=1;i<prices.length;i++){
                if(prices[i]>prices[i-1]){
                    sum+=prices[i]-prices[i-1];                    
                }
            }
            return sum;
        }
        int[][] sell=new int[k+1][prices.length];
        for(int i=1;i<k+1;i++){
            int temp=sell[i-1][0]-prices[0];
            for(int j=1;j<prices.length;j++){
                sell[i][j]=Math.max(sell[i][j-1],temp+prices[j]);
                temp=Math.max(temp,sell[i-1][j-1]-prices[j]);
            }
        }
        return sell[k][prices.length-1];
    }
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这题比121. Best Time to Buy and Sell Stock ,122. Best Time to Buy and Sell Stock II 难了不知道多少倍。

只能看讨论区大神的解答了。知道了可以用二维dp做。dp[i][j]中i代表交易次数,j代表了到那一天,dp[i][j]代表了此时的最大利润。关键是递推式怎么得到???

可以分两种情况:

1.当最后一天没有卖出股票时。dp[i][j]=dp[i][j-1]。

2.当最后一天卖出股票时。dp[i][j]=max(dp[i-1][jpre]+prices[j]-prices[jpre]),jpre是之前某一天的价格,这里的意思是,jpre天之前交易了i-1次,最后一次交易为prices[j]-prices[jpre]。这样每次还要计算最大的jpre值??把上式改造一下为dp[i][j]=prices[j]+max(dp[i-1][jpre]-prices[jpre])。这样有一个好处,遍历j时能维护一个最大的dp[i-1][jpre]-prices[jpre],就不用每次都计算了。

所以总的递推式为dp[i][j] = max(dp[i][j-1] , prices[j]+max(dp[i-1][jpre]-prices[jpre]))。

初始条件:dp[0][j]=0;dp[i][0]=0。

这里还要考虑一个内存溢出的问题,当k值过大是k>prices.length/2时,问题转化为可以leetcode 122. Best Time to Buy and Sell Stock II 。可以进行任意多次交易。

public int maxProfit(int k, int[] prices) {
        if(prices==null||prices.length==0) return 0;
        if(k>prices.length/2) return maxProfit2(prices);//转换为问题2的情况
        int[][] dp=new int[k+1][prices.length];
        for(int i=1;i<k+1;i++){
            int maxtemp=dp[i-1][0]-prices[0];
            for(int j=1;j<prices.length;j++){//在变动的j时维护一个max(dp[k-1][j]-prices[j])
                dp[i][j]=Math.max(dp[i][j-1],prices[j]+maxtemp);
                maxtemp=Math.max(maxtemp,dp[i-1][j]-prices[j]);
            }
        }
        return dp[k][prices.length-1];
    }
    
    public int maxProfit2(int[] prices){
        int sum=0;
        for(int i=1;i<prices.length;i++){
            if(prices[i]>prices[i-1]){
                sum+=prices[i]-prices[i-1];
            } 
        }
        return sum;
    }

这题没想到解法,主要是没想到递推式。递推式要分析各种情况,但是如何找到各种情况?有时可以考虑当前问题的最后一步的各种情况。从最后一步入手,这里就是最后一天的股票的处理,通过分析两种情况和子问题建立了联系。这和编辑距离那一题有点像,那题是考虑最后一个字符的各种处理情况入手的。

这题的还有一个难点是求解递推式时的一个技巧,不用再一次遍历求max(dp[i-1][jpre]-prices[jpre]),在遍历时自动更新就行了,这也是减少了重复计算。减少重复计算往往也是减少时间复杂度的本质。需要用心体会。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值