zoukankan      html  css  js  c++  java
  • LeetCode(123):买卖股票的最佳时机 III

    Hard!

    题目描述:

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

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

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

    示例 1:

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

    示例 2:

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

    示例 3:

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

    解题思路:

    这道是买股票的最佳时间系列问题中最难最复杂的一道,前面两道Best Time to Buy and Sell Stock 买卖股票的最佳时间Best Time to Buy and Sell Stock II 买股票的最佳时间之二的思路都非常的简洁明了,算法也很简单。

    而这道是要求最多交易两次,找到最大利润,还是需要用动态规划Dynamic Programming来解,而这里我们需要两个递推公式来分别更新两个变量local和global,参见https://blog.csdn.net/linhuanmars/article/details/23236995,

    我们其实可以求至少k次交易的最大利润,找到通解后可以设定 k = 2,即为本题的解答。我们定义local[i][j]为在到达第i天时最多可进行j次交易并且最后一次交易在最后一天卖出的最大利润,此为局部最优。然后我们定义global[i][j]为在到达第i天时最多可进行j次交易的最大利润,此为全局最优。它们的递推式为:

    local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff)

    global[i][j] = max(local[i][j], global[i - 1][j])

    其中局部最优值是比较前一天并少交易一次的全局最优加上大于0的差值,和前一天的局部最优加上差值中取较大值,而全局最优比较局部最优和前一天的全局最优。

    C++解法一:

     1 class Solution {
     2 public:
     3     int maxProfit(vector<int> &prices) {
     4         if (prices.empty()) return 0;
     5         int n = prices.size(), g[n][3] = {0}, l[n][3] = {0};
     6         for (int i = 1; i < prices.size(); ++i) {
     7             int diff = prices[i] - prices[i - 1];
     8             for (int j = 1; j <= 2; ++j) {
     9                 l[i][j] = max(g[i - 1][j - 1] + max(diff, 0), l[i - 1][j] + diff);
    10                 g[i][j] = max(l[i][j], g[i - 1][j]);
    11             }
    12         }
    13         return g[n - 1][2];
    14     }
    15 };

    下面这种解法用一维数组来代替二维数组,可以极大地节省空间,由于覆盖的顺序关系,我们需要j从2到1,这样可以取到正确的g[j-1]值,而非已经被覆盖过的值。

    C++解法二:

     1 class Solution {
     2 public:
     3     int maxProfit(vector<int> &prices) {
     4         if (prices.empty()) return 0;
     5         int g[3] = {0};
     6         int l[3] = {0};
     7         for (int i = 0; i < prices.size() - 1; ++i) {
     8             int diff = prices[i + 1] - prices[i];
     9             for (int j = 2; j >= 1; --j) {
    10                 l[j] = max(g[j - 1] + max(diff, 0), l[j] + diff);
    11                 g[j] = max(l[j], g[j]);
    12             }
    13         }
    14         return g[2];
    15     }
    16 };

    我们如果假设prices数组为1, 3, 2, 9, 那么我们来看每次更新时local 和 global 的值:

    第一天两次交易:      第一天一次交易:

    local:    0 0 0       local:    0 0 0 

    global:  0 0 0       global:  0 0 0

    第二天两次交易:      第二天一次交易:

    local:    0 0 2       local:    0 2 2 

    global:  0 0 2       global:  0 2 2

    第三天两次交易:      第三天一次交易:

    local:    0 2 2       local:    0 1 2 

    global:  0 2 2       global:  0 2 2

    第四天两次交易:      第四天一次交易:

    local:    0 1 9       local:    0 8 9 

    global:  0 2 9       global:  0 8 9

    在网友@loveahneehttps://home.cnblogs.com/u/1221269/的提醒下,发现了其实上述的递推公式关于local[i][j]的可以稍稍化简一下,我们之前定义的local[i][j]为在到达第i天时最多可进行j次交易并且最后一次交易在最后一天卖出的最大利润,然后网友@fgvltyhttps://home.cnblogs.com/u/985421/解释了一下第 i 天卖第 j 支股票的话,一定是下面的一种:

    1. 今天刚买的
    那么 Local(i, j) = Global(i-1, j-1)
    相当于啥都没干

    2. 昨天买的
    那么 Local(i, j) = Global(i-1, j-1) + diff
    等于Global(i-1, j-1) 中的交易,加上今天干的那一票

    3. 更早之前买的
    那么 Local(i, j) = Local(i-1, j) + diff
    昨天别卖了,留到今天卖

    但其实第一种情况是不需要考虑的,因为当天买当天卖不会增加利润,完全是重复操作,这种情况可以归纳在global[i-1][j-1]中,所以我们就不需要max(0, diff)了,那么由于两项都加上了diff,所以我们可以把diff抽到max的外面,所以更新后的递推公式为:

    local[i][j] = max(global[i - 1][j - 1], local[i - 1][j]) + diff

    global[i][j] = max(local[i][j], global[i - 1][j])

  • 相关阅读:
    MagicZoom bug-Strict Standards: Only variables should be assigned by reference Error
    Web大文件(夹)上传(断点续传)控件-Xproer.HttpUploader6
    在PHP中,通过filesize函数可以取得文件的大小,文件大小是以字节数表示的。如果要转换文件大小的单位,可以自己定义函数来实现。
    PHP正则匹配6到16位字符组合(且只能为数字、字母、下划线)
    Windows下PHP版本选取
    简单配置nginx使之支持pathinfo
    PHP如何关闭notice级别的错误提示
    php开启pathinfo 模式
    php 5.3新增的闭包语法介绍function() use() {}
    Object.prototype.toString.call() 区分对象类型
  • 原文地址:https://www.cnblogs.com/ariel-dreamland/p/9166176.html
Copyright © 2011-2022 走看看