• 浅谈什么是动态规划以及相关的「股票」算法题


    640?wx_fmt=jpeg

    动态规划

    1 概念

      动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。在学习动态规划之前需要明确掌握几个重要概念。

      阶段:对于一个完整的问题过程,适当的切分为若干个相互联系的子问题,每次在求解一个子问题,则对应一个阶段,整个问题的求解转化为按照阶段次序去求解。

      状态:状态表示每个阶段开始时所处的客观条件,即在求解子问题时的已知条件。状态描述了研究的问题过程中的状况。

      决策:决策表示当求解过程处于某一阶段的某一状态时,可以根据当前条件作出不同的选择,从而确定下一个阶段的状态,这种选择称为决策。

      策略:由所有阶段的决策组成的决策序列称为全过程策略,简称策略。

      最优策略:在所有的策略中,找到代价最小,性能最优的策略,此策略称为最优策略。

      状态转移方程:状态转移方程是确定两个相邻阶段状态的演变过程,描述了状态之间是如何演变的。

    2 使用场景

    能采用动态规划求解的问题的一般要具有 3 个性质:

      (1)最优化:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。子问题的局部最优将导致整个问题的全局最优。换句话说,就是问题的一个最优解中一定包含子问题的一个最优解。

      (2)无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关,与其他阶段的状态无关,特别是与未发生的阶段的状态无关。

       (3)重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

    3 算法流程

      (1)划分阶段:按照问题的时间或者空间特征将问题划分为若干个阶段。
      (2)确定状态以及状态变量:将问题的不同阶段时期的不同状态描述出来。
      (3)确定决策并写出状态转移方程:根据相邻两个阶段的各个状态之间的关系确定决策。
      (4)寻找边界条件:一般而言,状态转移方程是递推式,必须有一个递推的边界条件。
      (5)设计程序,解决问题

    实战练习

    下面的三道算法题都是来源于 LeetCode 上与股票买卖相关的问题 ,我们按照 动态规划 的算法流程来处理该类问题。

    股票买卖这一类的问题,都是给一个输入数组,里面的每个元素表示的是每天的股价,并且你只能持有一支股票(也就是你必须在再次购买前出售掉之前的股票),一般来说有下面几种问法:

    • 只能买卖一次

    • 可以买卖无数次

    • 可以买卖 k 次

    需要你设计一个算法去获取最大的利润。

    买卖股票的最佳时机

    题目来源于 LeetCode 上第 121 号问题:买卖股票的最佳时机。题目难度为 Easy,目前通过率为 49.4% 。

    题目描述

    给定一个数组,它的第 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。

    题目解析

    我们按照动态规划的思想来思考这道问题。

    状态

    买入(buy)卖出(sell) 这两种状态。

    转移方程

    对于买来说,买之后可以卖出(进入卖状态),也可以不再进行股票交易(保持买状态)。

    对于卖来说,卖出股票后不在进行股票交易(还在卖状态)。

    只有在手上的钱才算钱,手上的钱购买当天的股票后相当于亏损。也就是说当天买的话意味着损失-prices[i],当天卖的话意味着增加prices[i],当天卖出总的收益就是 buy+prices[i]

    所以我们只要考虑当天买和之前买哪个收益更高,当天卖和之前卖哪个收益更高。

    • buy = max(buy, -price[i])  (注意:根据定义 buy 是负数)

    • sell = max(sell,  prices[i] + buy)

    边界

    第一天 buy = -prices[0], sell = 0,最后返回 sell 即可。

    代码实现

    
     

    //程序员小吴
    class Solution {
        public int maxProfit(int[] prices) {
            if(prices.length <= 1)
                return 0;
            int buy = -prices[0], sell = 0;
            for(int i = 1; i < prices.length; i++) {
                buy = Math.max(buy, -prices[i]);
                sell = Math.max(sell, prices[i] + buy);

            }
            return sell;
        }
    }

    买卖股票的最佳时机 II

    题目来源于 LeetCode 上第 122 号问题:买卖股票的最佳时机 II。题目难度为 Easy,目前通过率为 53.0% 。

    题目描述

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

    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

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

    示例 1:

    
     

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

    题目解析

    状态

    买入(buy)卖出(sell) 这两种状态。

    转移方程

    对比上题,这里可以有无限次的买入和卖出,也就是说 买入 状态之前可拥有 卖出 状态,所以买入的转移方程需要变化。

    • buy = max(buy, sell - price[i])

    • sell = max(sell,   buy + prices[i] )

    边界

    第一天 buy = -prices[0], sell = 0,最后返回 sell 即可。

    代码实现

    
     

    //程序员小吴
    class Solution {
        public int maxProfit(int[] prices) {
            if(prices.length <= 1)
                return 0;
            int buy = -prices[0], sell = 0;
            for(int i = 1; i < prices.length; i++) {
                sell = Math.max(sell, prices[i] + buy);
                buy = Math.max( buy,sell - prices[i]);
            }
            return sell;
        }
    }

    买卖股票的最佳时机 III

    题目来源于 LeetCode 上第 123 号问题:买卖股票的最佳时机 III。题目难度为 Hard,目前通过率为 36.1% 。

    题目描述

    给定一个数组,它的第 i 个元素是一支给定的股票在第 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。

    题目解析

    这里限制了最多两笔交易。

    状态

    第一次买入(fstBuy)第一次卖出(fstSell)第二次买入(secBuy)第二次卖出(secSell) 这四种状态。

    转移方程

    这里可以有两次的买入和卖出,也就是说 买入 状态之前可拥有 卖出 状态,所以买入和卖出的转移方程需要变化。

    • fstBuy = max(fstBuy ,  -price[i])

    • fstSell = max(fstSell,fstBuy + prices[i] )

    • secBuy = max(secBuy ,fstSell -price[i]) (受第一次卖出状态的影响)

    • secSell = max(secSell ,secBuy + prices[i] )

    边界

    • 一开始 fstBuy = -prices[0]

    • 买入后直接卖出,fstSell = 0

    • 买入后再卖出再买入,secBuy - prices[0]

    • 买入后再卖出再买入再卖出,secSell = 0

    最后返回 secSell 。

    代码实现

    
     

    //程序员小吴
    class Solution {
        public int maxProfit(int[] prices) {
            int fstBuy = Integer.MIN_VALUE, fstSell = 0;
            int secBuy = Integer.MIN_VALUE, secSell = 0;
            for(int i = 0; i < prices.length; i++) {
                fstBuy = Math.max(fstBuy, -prices[i]);
                fstSell = Math.max(fstSell, fstBuy + prices[i]);
                secBuy = Math.max(secBuy, fstSell -  prices[i]);
                secSell = Math.max(secSell, secBuy +  prices[i]); 
            }
            return secSell;

        }
    }




    往期热文

    拜托,面试官别问我「布隆」了

    有点难度,几道和「滑动窗口」有关的算法面试题

    荐阅读

    几道和「二叉树」有关的算法面试题
    几道和「堆栈、队列」有关的面试算法题

    链表算法面试问题?看我就够了!

    一道看完答案你会觉得很沙雕的「动态规划算法题」

    如何有效的写算法题

    来和程序员小吴一起学算法~

    640?wx_fmt=jpeg


    我就知道你在看!
  • 相关阅读:
    点击劫持漏洞之理解 python打造一个挖掘点击劫持漏洞的脚本
    URL重定向漏洞,python打造URL重定向漏洞检测脚本
    动态链接库(DLL)
    vs不支持通过afxgetmainwnd()获取窗口句柄(转)
    HALCON学习-下载、安装
    HALCON学习-资料
    MFC,ADO方式实现数据库操作
    VS2010 EXCEL2010 表格操作的编程实现
    Git Compare with base,比较大文件时,长时间等待,无法加载
    VS2010编译VS2008工程时,LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt
  • 原文地址:https://www.cnblogs.com/csnd/p/16675396.html
Copyright © 2020-2023  润新知