动态规划方法解决股票买卖问题

213 阅读4分钟

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战

问题

给定一个整数数组prices,其中第  prices[i] 表示第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例1
输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

示例2
输入: prices = [1]
输出: 0

分析

动态规划所处理的问题是一个多阶段决策问题,从初始状态开始,中间通过一系列的决策,最终形成一个最优解。 在这题中我们初始状态是0利润,要求经过中间的一系列的决策,最终求的最大利润,所以我们可以考虑使用动态规划的方法来求这个问题。

状态

首先分析一下这个题目所包含的状态,因为里面有一个冷冻期的概念,而且每次我们可以选择买入或者卖出,所以我们可以把它分成三种状态:

  1. 手上持有股票的最大收益
  2. 手上不持有股票,并且处于冷冻期中的累计最大收益
  3. 手上不持有股票,并且不在冷冻期中的累计最大收益 所以我们需要用一个二维数组表示,其中dp[i][0]dp[i][1]dp[i][2] 分别代表上面三种状态。

而到最后一天时,肯定是要把股票卖掉,才能获取最大利润的,所以最终结果从 dp[i][1]dp[i][2] 中取出。

定义初始值

第一天的时候,我们只能买入股票,所以dp[0][0] = -prices[0],当前利润为负,持有了股票,所以dp[0][1]dp[0][2] 都是 0

状态转移方程

我们每天可以选择买入或卖出,这是基于前一天的状态转移而来的,我们对三种状态分别分析

  1. 对于 dp[i][0] 目前这天结束后持有股票,所以有两种选择,第一种是前一天就买入了这只股票也就是 dp[i-1][0],第二种是当天买入了股票,dp[i-1][2] - prices[i](冷冻状态不能买入,所以选择dp[i-1][2]),我们选择利润比较大的方式,所以dp[i][0] = Math.max(dp[i-1][2] - prices[i], dp[i-1][0])

  2. 对于 dp[i][1] 当天结束后也就是第i天,处于冷冻状态,所以只有一种情况,就是前一天卖出了股票 dp[i][1] = dp[i-1][0] + prices[i]

  3. 对于 dp[i][2] 因为当天不持有股票并且不处于冷冻期,说明当天没有任何操作;如果前一天是冷冻期,那么到了今天就不是冷冻期了所以可以选择dp[i-1][1],或者前一天就不是冷冻期那就是dp[i-1][2],所以公式为dp[i][2] = Math.max(dp[i-1][1], dp[i-1][2]);

代码

经过上面的分析,我们得到了 dp 数组和它的初始值,以及过程中的状态转移方程。所以得到下面的代码

var maxProfit = function(prices) {
  const n = prices.length;
  const dp = new Array(n);
  for (let i = 0; i < n; i++) {
    dp[i] = new Array(3)    
  }
  dp[0][0] = -prices[0];
  dp[0][1] = 0;
  dp[0][2] = 0;
  for (let i = 1; i < n; i++) {
    // dp[i][0] 手上持有股票的最大收益
    // dp[i][1] 手上不持有股票,并且处于冷冻期中的累计最大收益
    // dp[i][2] 手上不持有股票,并且不在冷冻期中的累计最大收益
    dp[i][0] = Math.max(dp[i-1][2] - prices[i], dp[i-1][0]) // 前一天不在冷冻期今天可以买入,或者前一天已经持有股票的收益
    dp[i][1] = dp[i-1][0] + prices[i]; // 前一天卖出股票,这天就变成了冷冻期,只有一种情况
    dp[i][2] = Math.max(dp[i-1][1], dp[i-1][2]); // 因为前一天冷冻期收益在这天就变成了不在冷冻期
  }
  return Math.max(dp[n-1][1], dp[n-1][2])
};