zoukankan      html  css  js  c++  java
  • 买卖股票的最佳时机--动态规划

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

    如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

    注意你不能在买入股票前卖出股票。

    示例 1:

    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
         注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

    示例2:

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

    解法一: 暴力解决法

    算法思想:

    我们需要找出给定数组中两个数字之间的最大差值(即,最大利润)。此外,第二个数字(卖出价格)必须大于第一个数字(买入价格)。

    对于每组 i 和 j (j > i)我们需要找出max(prices[j] - prices[i])

    代码如下:

    func maxProfit(_ prices: [Int]) -> Int {
        guard prices.count > 0 else {return 0}
        var maxProfit: Int = 0
        for i in 0..<prices.count - 1 {
            for j in i+1..<prices.count {
                maxProfit = max(maxProfit, prices[j] - prices[i])
            }
        }
        return maxProfit
    }

    解法二:动态规划

    算法思想:

    动态规划的3个步骤:

    1. 设定状态
    2. 推导方程
    3. 起始值和输出

    这里状态的设置得稍微想一想,数组的问题一般我们得固定其中一个变量。这道题有两个变量,一个是“买入”,另一个是“卖出”,这里我们固定“卖出”变量,“买入”变量一定在它之前。

    因此状态dp[i]表示:在索引为 i 的这一天,用户所能获得的最大利润。

    下面思考状态转移,我们就要想dp [i] 的从前面的状态值转移过来,但是我们就会发现新的问题dp[i - 1]、dp[i - 2]、……、dp[0],状态转移无从下手。这里就说明:状态还不够,得把“买入”和“卖出”操作加入到状态的设置中。

    于是下面修改状态的定义:

    • dp[i][0]表示:在索引为 i 的这一天,用户手上不持股所能获得的最大利润;
    10 一般表示没有,在索引为 i 的这一天,手上没有股票,语义上也是清晰的,下面对状态 1 的理解也是一样;
    2、   “用户手上不持股”不代表用户一定在索引为 i 的这一天把股票抛售了;
    3、    在索引为 i 的这一天具体应该这样理解:从索引为 0 的天数开始,到索引为 i 的这一天,因此,这个状态的设置具有“前缀”的意味,因此输出是 dp[len - 1][0],不可能是 dp[len - 1][1],在只发生一次交易的情况下,持有这支股票一定不能使我们获得最大利润。
    • 状态dp[i][1]表示:在索引为 i 的这一天,用户手上持股所能获得最大的利润。

    下面考虑状态转移方程:

    1. dp[i][0] 可以由谁转移过来?
    • dp[i - 1][0] :今天不持股,假设我今天什么都不操作,当然可以从昨天不持股转移过来,这一点是显然的
    • dp[i - 1][1] + prices[i]:昨天持股,就在索引为 i 的这一天,我卖出了股票,状态由 1 变成了 0,此时获得利润,因此加上这一天的股价。

      综上:dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);

      2. dp[i][1] 可以由谁转移过来?

    • dp[i - 1][1] :今天持股,假设我今天什么都不操作,当然可以从昨天持股转移过来,这一点是显然的;

    • -prices[i]:**请注意:**状态 1 不能由状态 0 来,因为事实上,状态 0 特指:“卖出股票以后不持有股票的状态”,请注意这个状态和“没有进行过任何一次交易的不持有股票的状态”的区别。

      因此,-prices[i] 就表示,在索引为 i 的这一天,执行买入操作得到的收益,再次强调:因为题目只允许一次交易,因此不能加上 dp[i - 1][0]

    综上:dp[i][1] = max(dp[i - 1][1], -prices[i]);

    在新修改的定义中,我们已经分析出了输出dp[len - 1][0]

    public int maxProfit(int[] prices) {
            int len = prices.length;
            if (len < 2) {
                return 0;
            }
    
            // 0:用户手上不持股所能获得的最大利润,特指卖出股票以后的不持股,非指没有进行过任何交易的不持股
            // 1:用户手上持股所能获得的最大利润
    
            // 注意:因为题目限制只能交易一次,因此状态只可能从 1 到 0,不可能从 0 到 1
            // 状态转移方程:
            // dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
            // dp[i][1] = max(dp[i - 1][1], -prices[i])
            int[][] dp = new int[len][2];
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
            for (int i = 1; i < len; i++) {
                dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
                dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
            }
            return dp[len - 1][0];
        }

    以上就是股票最大收益算法,希望对大家有所帮助!!! 

  • 相关阅读:
    【LeetCode-树】找树左下角的值
    【LeetCode-贪心】合并区间
    MongoDB3.6版本新增特性
    MongoDB initial sync过程
    MongoDB3.4版本新增特性
    保险配置原则
    MongoDB批量操作时字段为null时没有入库
    Kafka消费者没有收到通知的分析
    Git分支的管理
    NoSQLBooster如何MongoDB的部分文档从一个集合拷贝到另外一个集合中
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/11837218.html
Copyright © 2011-2022 走看看