188. Best Time to Buy and Sell Stock IV(买卖股票的最佳时机 IV)

假设你有一个数组,其中第 i 个元素是第 i 天给定股票的价格。

设计一个算法来找到最大的利润。您最多可以完成 k 笔交易。

注意:

你不可以同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

这道题不像前面的不限交易次数,要求交易次数在k次以内,那么就不能有获利就交易,选择交易时机比较关键,比如[2,3,1,2,6,2,6,7],限制交易2次,如果取到[1,7],那么就漏掉了中间两个[2,6,2,6],就不是最佳方案。所以还是应该尝试动态规划从局部最优找到全局最优。

此题最难的感觉还是写出递推方程,最终还是参考其他博客的递推方程:
mustSell[i][j]表示前i个交易日最多交易j次能达到的最大盈利,其中第i日必须进行了至多第j次交易。
globalSell[i][j]表示前i个交易日最多交易j次能达到的最大盈利,到第i日必须完成了至多第j次交易
递推方程如下:

int profit = prices[i] - prices[i-1];
profit = profit >= 0 ? profit : 0;
mustSell[i][j]= Math.max(globalSell[i-1][j-1] + profit,mustSell[i-1][j] + prices[i] - prices[i-1]);
globalSell[i][j] = Math.max(globalSell[i-1][j],mustSell[i][j]);

profit 表示第i天买入,第i-1天卖出的盈利情况,如果profit<0,则profit=0

mustSell[i][j]分为两种情况:

1.mustSell[i][j] = globalSell[i-1][j-1] + profit
前i-1天最多进行交易j-1次,剩下一次完整的交易留给你,就看你在第i天的表现了,必须进行,那么这时如果还要交易,可以第i-1再买,第i卖,但是这有可能是亏钱的,如果是亏钱,那就第i天买同时第i天卖,好过亏钱,所以有profit = max(0,prices[i] - prices[i-1]);

2.mustSell[i][j] = mustSell[i-1][j] + prices[i] - prices[i-1]
前i-1天最多进行交易j次,且第i-1天必须进行第j次交易,而又有mustSell[i][j],那么意思第i-1天买、第i天卖,都是进行了第j次交易,因此有 prices[i] - prices[i-1]

globalSell[i][j] = Math.max(globalSell[i-1][j],mustSell[i][j])
这个就比较好理解:要么第i-1天完成第j次交易就是最佳策略了;要么就是第i天有进行交易能让结果更优。

public int maxProfit(int k, int[] prices) {
        if(k < 1){
            return 0;
        }
        if(prices.length < 2){
            return 0;
        }
        int len = prices.length;
        if(k*2 >= len-1){//买卖次数足够多
            return maxProfit0(prices);
        }
        if(k == 2){
            return maxProfit2(prices);
        }
        int mustSell[][] = new int [len][k+1];
        int globalSell[][] = new int [len][k+1];

        for(int j=1;j<=k;j++){
            for(int i=1;i<len;i++){//交易k次
                int profit = prices[i] - prices[i-1];
                profit = profit >= 0 ? profit : 0;
                mustSell[i][j] = Math.max(globalSell[i-1][j-1] + profit,mustSell[i-1][j] + prices[i] - prices[i-1]);
                globalSell[i][j] = Math.max(globalSell[i-1][j],mustSell[i][j]);
            }
        }

        return globalSell[prices.length-1][k];
    }
    public int maxProfitk(int dp[][],int k,int start,int count) {
        if(k == 0 || start >= dp.length - 1){
            return count;
        }
        int result = 0;
        for(int i=start;i<dp.length;i++){
            for(int j=i+1;j<dp.length;j++){
                if(dp[i][j] > 0){
                    int temp = maxProfitk(dp,k-1,j+1,dp[i][j] + count);
                    result = temp > result ? temp : result;
                }
            }
        }
        return result;
     }
    public int maxProfit0(int[] prices) {
        if(prices.length < 2){
            return 0;
        }
        int len = prices.length;
        int result = 0;
        for(int i=1;i<len;i++){
            if(prices[i] > prices[i-1]){
                result += prices[i] - prices[i-1];
            }
        }

        return result;
     }

    public int maxProfit2(int[] prices) {
         int len = prices.length;
         if(len<=1)return 0;
         int max=0;

         int dp1[] = new int[len];
           int curmin= prices[0];
           for(int i=1;i<len;i++){
               max = max < prices[i] - curmin ? prices[i] - curmin : max;
               curmin =  prices[i] < curmin ?  prices[i] : curmin;
               dp1[i] = max;
           }

           int curmax= prices[len-1];
           max=0;
           int dp2[] = new int[len];
           for(int i=len-1;i>=0;i--){
               max = max < curmax-prices[i] ? curmax-prices[i]  : max;
               curmax =  prices[i] > curmax ?  prices[i] : curmax;
               dp2[i] = max;
           }
           max=0;
           for(int i=len-1;i>0;i--){
               max = max < dp1[i]+dp2[i] ? dp1[i]+dp2[i]  : max;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值