买卖股票的最佳时机--动态规划

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

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

原文地址:https://www.cnblogs.com/guohai-stronger/p/11837218.html