「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
问题
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例1
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例2
输入: prices = [1]
输出: 0
分析
动态规划所处理的问题是一个多阶段决策问题,从初始状态开始,中间通过一系列的决策,最终形成一个最优解。 在这题中我们初始状态是0利润,要求经过中间的一系列的决策,最终求的最大利润,所以我们可以考虑使用动态规划的方法来求这个问题。
状态
首先分析一下这个题目所包含的状态,因为里面有一个冷冻期的概念,而且每次我们可以选择买入或者卖出,所以我们可以把它分成三种状态:
- 手上持有股票的最大收益
- 手上不持有股票,并且处于冷冻期中的累计最大收益
- 手上不持有股票,并且不在冷冻期中的累计最大收益
所以我们需要用一个二维数组表示,其中
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
状态转移方程
我们每天可以选择买入或卖出,这是基于前一天的状态转移而来的,我们对三种状态分别分析
-
对于
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]) -
对于
dp[i][1]当天结束后也就是第i天,处于冷冻状态,所以只有一种情况,就是前一天卖出了股票dp[i][1] = dp[i-1][0] + prices[i] -
对于
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])
};