• 买卖股票专题系列3---买卖股票的最佳时机3


    题目:

      给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    题解:

      前面我们一起学习了买卖股票1、2的算法,分别对应只能交易1次,可以无限次交易的最大利润。但是该题最多交易2次(可以交易0次、1次、2次),此时的情况就有点复杂了,这时候就不能用贪心思想来解决了,要用动态规划算法了。

      动态规划解题的2个步骤:

      1.状态定义

        对于股票的状态有2种,持有、不持有。即买入和卖出。

      2.状态转移方程

        此题的状态转移方程有点复杂,中等难度的动态规划题目的记忆数组memo(即dp数组)一般是一维、二维数组,但是此题要用到三维数组。这也是为什么此题是一道困难题目的原因。转移方程如下:

        记忆三维数组 memo定义:memo[天数][是否持有股票][卖出次数]

        

        memo[i][0][0] = memo[i-1][0][0];//什么也不做
    
        //未持股但卖出了1次
    
        memo[i][0][1] = Math.max(memo[i-1][0][1],memo[i-1][1][0] + prices[i]);
        //未持股但卖出了2次
        memo[i][0][2] = Math.max(memo[i-1][0][2], memo[i-1][1][1] + prices[i]);
        //持股并且卖出了0次
        memo[i][1][0] = Math.max(memo[i-1][1][0], memo[i-1][0][0] - prices[i]);
        //持股并且卖出了1次
        memo[i][1][1] = Math.max(memo[i-1][1][1], memo[i-1][0][1] - prices[i]);
        //持股并且卖出了1次,此情况不会存在
        memo[i][1][2] = Integer.MIN_VALUE / 2;

      

    下面直接上代码:

    解法1:三维数组memo

    public int maxProfit(int[] prices) {
            if(prices == null || prices.length < 2) return 0;
            int len = prices.length;
            //三维数组 memo[天数][是否持有股票][卖出次数]
            int[][][] memo = new int[len][2][3];
            memo[0][0][0] = 0;//0次交易后不持有股票的利润
            memo[0][0][1] = Integer.MIN_VALUE/2 ;//此情况不存在,除以2防止越界
            memo[0][1][0] = -prices[0];;
            memo[0][1][1] = Integer.MIN_VALUE/2;//此情况不存在
            memo[0][1][2] = Integer.MIN_VALUE/2;//此情况不存在
            //memo[0][2][1] = -prices[0];
            for(int i=1;i<len;i++){
                memo[i][0][0] = memo[i-1][0][0];//什么也不做
                //未持股但卖出了1次
                memo[i][0][1] = Math.max(memo[i-1][0][1],memo[i-1][1][0] + prices[i]);
                //未持股但卖出了2次
                memo[i][0][2] = Math.max(memo[i-1][0][2], memo[i-1][1][1] + prices[i]);
                //持股并且卖出了0次
                memo[i][1][0] = Math.max(memo[i-1][1][0], memo[i-1][0][0] - prices[i]);
                //持股并且卖出了1次
                memo[i][1][1] = Math.max(memo[i-1][1][1], memo[i-1][0][1]  - prices[i]);
                //持股并且卖出了1次,此情况不会存在
                memo[i][1][2] = Integer.MIN_VALUE / 2;
            }
            //未持股时利润才会更大
            int a = Math.max(memo[len-1][0][0] , memo[len-1][0][1]);
            int b = Math.max(a,memo[len-1][0][2]);
            return Math.max(b, 0);
        }

    解法2:二维数组(三维数组的空间优化版)

       
    public int maxProfit(int[] prices) {
            if(prices == null || prices.length < 2) return 0;
            int len = prices.length;
            //二维数组 memo[天数][状态]
            int[][] memo= new int[len][5];
            //0 未持股但卖出了0次(什么也不做),1 未持股但卖出了1次,2 未持股但卖出了2次, 3 持股并且卖出了0次,4 持股并且卖出了1次
            //第一天
            memo[0][0] = 0;
            memo[0][1] = Integer.MIN_VALUE /2;//此情况不存在,除以2防止越界
            memo[0][2] = Integer.MIN_VALUE / 2;//此情况不存在,除以2防止越界
            memo[0][3] = -prices[0];
            memo[0][4] = Integer.MIN_VALUE / 2;//此情况不存在,除以2防止越界
            
            for(int i=1;i<len;i++){
                //未持股但卖出了0次
                memo[i][0] = memo[i-1][0];//什么也不做,不买也不卖
                //未持股但卖出了1次
                memo[i][1] = Math.max(memo[i-1][1],memo[i-1][3] + prices[i]);
                //未持股但卖出了2次
                memo[i][2] = Math.max(memo[i-1][2], memo[i-1][4] + prices[i]);
                //持股并且卖出了0次
                memo[i][3] = Math.max(memo[i-1][3], memo[i-1][0] - prices[i]);
                //持股并且卖出了1次
                memo[i][4] = Math.max(memo[i-1][4], memo[i-1][1]  - prices[i]);
                
            }
            //未持股时利润才会更大
            int a = Math.max(memo[len-1][0] , memo[len-1][1]);
            int b = Math.max(a,memo[len-1][2]);
            return Math.max(b, 0);
        }
    解法3:终极空间优化版,该解法不好理解,建议先把解法1,解法2理解后再来看这个解法。
    Java版本:
    public int maxProfit(int[] prices) {
            /**
            对于任意一天考虑四个变量:
            fstBuy: 在该天第一次买入股票可获得的最大收益 
            fstSell: 在该天第一次卖出股票可获得的最大收益
            secBuy: 在该天第二次买入股票可获得的最大收益
            secSell: 在该天第二次卖出股票可获得的最大收益
            分别对四个变量进行相应的更新, 最后secSell就是最大
            收益值(secSell >= fstSell)
            **/
            int fstBuy = Integer.MIN_VALUE, fstSell = 0;
            int secBuy = Integer.MIN_VALUE, secSell = 0;
            for(int p : prices) {
                fstBuy = Math.max(fstBuy, -p);
                fstSell = Math.max(fstSell, fstBuy + p);
                secBuy = Math.max(secBuy, fstSell - p);
                secSell = Math.max(secSell, secBuy + p); 
            }
            return secSell;
        }
    JS版本:
      
    var maxProfit = function(prices) {
        //动态规划
        if(prices == null || prices.length == 1) return 0;
        
        /**
        对于任意一天考虑四个变量:
        fstBuy: 在该天第一次买入股票可获得的最大收益 
        fstSell: 在该天第一次卖出股票可获得的最大收益
        secBuy: 在该天第二次买入股票可获得的最大收益
        secSell: 在该天第二次卖出股票可获得的最大收益
        分别对四个变量进行相应的更新, 最后secSell就是最大
        收益值(secSell >= fstSell)
        **/
        let fstBuy = -Infinity, fstSell = 0;
        let secBuy = -Infinity, secSell = 0;
        for(let p of prices) {
            fstBuy = Math.max(fstBuy, -p);
            fstSell = Math.max(fstSell, fstBuy + p);
            secBuy = Math.max(secBuy, fstSell - p);
            secSell = Math.max(secSell, secBuy + p); 
        }
    
        return secSell;   
    };
  • 相关阅读:
    .net从后台返回js的提示框
    使用IntelliTrace的独立收集器帮助测试应用程序
    NuGet笔记
    使用.dmp+vs分析异常
    windbg笔记
    C# 删除文件、文件到到回收站及异常判断
    .Net利用反射调用DLL时,被调用DLL引用其它库问题
    C#中设置窗口圆角样式
    C# 中引用IHTMLDocument2
    c# 单实例运行
  • 原文地址:https://www.cnblogs.com/bobobjh/p/14414838.html
Copyright © 2020-2023  润新知