LeetCode 123题: 买卖股票的最佳时机 III(原创)

【题目描述】

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

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

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

输入:prices = [1]
输出:0

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

【题目链接】:. - 力扣(LeetCode)

【解题代码】

package dp;

public class MaxProfit3 {
    public static void main(String[] args) {
        //int[] prices = {1, 2, 3, 4, 5};
        int[] prices = {2, 1, 2, 0, 1};
        int result = new MaxProfit3().maxProfit(prices);
        System.out.println("计算结果:" + result);
    }

    public int maxProfit(int[] prices) {
        int[][][] dp = new int[prices.length][2][3];
        dp[0][0][0] = 0;
        dp[0][0][1] = 0;
        dp[0][0][2] = 0;
        dp[0][1][0] = -prices[0];
        dp[0][1][1] = -prices[0];
        dp[0][1][2] = -prices[0];
        for (int i = 1; i < prices.length; ++i) {
            dp[i][0][0] = 0;
            dp[i][0][1] = Math.max(dp[i - 1][0][1], dp[i - 1][1][0] + prices[i]);
            dp[i][0][2] = Math.max(dp[i - 1][0][2], dp[i - 1][1][1] + prices[i]);
            dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][0][0] - prices[i]);
            dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][1] - prices[i]);
            dp[i][1][2] = Math.max(dp[i - 1][1][2], dp[i - 1][0][2] + prices[i]);
        }
        return dp[prices.length - 1][0][2];
    }
}

【解题思路】

之前做了相关题目买卖股票的最佳时机买卖股票的最佳时机II,并都写了博客做了分析,以为这道题应该不难,结果折腾了好久还是没有思路。昨天在做完买卖股票的最佳时机含手续费时,突然有了思路:之前的题目根据当天有无股票的情况,将一维dp数组扩展为二维数组 那么按照这个思路,是否可以再根据当天的投资次数对二维数组再次扩展然后计算呢,按照这个思路并进行尝试终于得到如下算法思路:

  1. 定义三维动态规划数组dp[n][2][3],第一维是天数,第二维是手上是否有股票,第三维是交易次数(0次,1次,2次);接下來对每天各种情况的收益值进行一一分析:
  2. 今天手里没有股票,且0次交易的最大获利肯定0;
  3. 今天手里没有股票,且1次交易的最大获利等于:昨天手里没有股票,且有一次交易和昨天0次交易且今天卖掉股票两者最大值
  4. 今天手里没有股票,且2次交易的最大获利等于:昨天手里没有股票且有两次交易,和昨天1次交易且今天卖掉股票两者最大值
  5. 今天手里有股票,且0次交易的最大获利等于:昨天手里股票且有1次交易,和昨天1次交易且今天买股票两者最大值
  6. 今天手里有股票,且1次交易的最大获利等于:昨天手里有股票且有1次交易,和昨天没有股票且有1次交易且今天买股票的两者最大值

        按照这个思路,很快完成代码编写,编译运行并提交成功     

【解题步骤】

  1. 定义三维动态规划数组dp
     int[][][] dp = new int[prices.length][2][3];
  2. 初始化第1天各种情况的最大获利值
    dp[0][0][0] = 0;
    dp[0][0][1] = 0;
    dp[0][0][2] = 0;
    dp[0][1][0] = -prices[0];
    dp[0][1][1] = -prices[0];
    dp[0][1][2] = -prices[0];
  3. 然后从第二天开始到最后一天依次计算各种情况的最大获利值
    for (int i = 1; i < prices.length; ++i) {
        // 今天手里没有股票,且0次交易的最大获利0
        dp[i][0][0] = 0;
        // 今天手里没有股票,且一次交易的最大获利:昨天手里没有股票,且有一次交易和昨天0次交易今天卖掉股票两者最大值
        dp[i][0][1] = Math.max(dp[i - 1][0][1], dp[i - 1][1][0] + prices[i]);
        // 今天手里没有股票,且两次交易的最大获利:昨天手里没有股票且有两次交易,和昨天1次交易今天卖掉股票两者最大值
        dp[i][0][2] = Math.max(dp[i - 1][0][2], dp[i - 1][1][1] + prices[i]);
        // 今天手里有股票,且0次交易的最大获利:昨天手里股票且有1次交易,和昨天1次交易今天买股票两者最大值
        dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][0][0] - prices[i]);
        // 今天手里有股票,且1次交易的最大获利:昨天手里有股票且有1次交易,和昨天没有股票且有1次交易然后今天买股票的两者最大值
        dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][1] - prices[i]);
        // 今天手里有股票,且2次交易的最大获利:昨天手里有股票且有2次交易,和昨天有股票,且1次交易然后今天卖掉股票两者最大值
        dp[i][1][2] = Math.max(dp[i - 1][1][2], dp[i - 1][0][2] + prices[i]);
    }
  4. 最后一天手里没有股票且交易两次的最大获利值就是最终结果
    return dp[prices.length - 1][0][2];

【思考总结】

  1. 动态规划算法是数据结构中一个很重要的算法,另外一般学校里教的都很浅薄,程序员一定要自学掌握其算法脉络,并能学会灵活运用;
  2. 无后效性,递推算法一般只需要考虑眼前最近一层,其它层一定不需要考虑。“未来与过去无关”;
  3. 这道题的关键点就是:根据当天的投资次数将二维数组扩展成三维数组,并依次根据前一天的计算数值,对每天的各种情况进行计算;
  4. LeetCode解题之前,一定不要看题解,看了就“破功”了!            
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值