题目描述
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 卖出股票后,
你无法在第二天买入股票 (即冷冻期为 1 天)。
示例
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
动态规划
此题中第i天的最大收益,与之前所做的操作息息相关。它必定包含最优子结构,以及重叠子问题。想要计算出第i天的最大收益,那么就要在第i-1天时也能计算出最大收益。
动态规划最重要的一步就是写出状态转移方程。在此题中看似有四种状态
- 状态1:不处于冷却期,且持有一张股票
- 状态2:不处于冷却期,且不持有股票
- 状态3:处于冷却期,且不持有股票
状态4:处于冷却期,且持有一张股票
实际上,仔细查看题目就可以知道,只有当卖出股票后才会进入冷却期,也就是说处于冷却期时,一定不持有股票。那么状态之间如何转换呢。显然,状态1可以由状态1转移,这种情况在操作上表现为,不买入也不卖出。状态1也可由状态2转移,这意味着买入了当天的股票。状态2可由状态2以及状态3转移,这两种转移方式都表示当天无买入或卖出的操作。状态3可由状态1转移,意味着当天卖出了股票。我们可以用一个包含三个一维数组的二维数组来储存这三种状态。
public int maxProfit(int[] prices) {
if (prices == null || prices.length < 1) {
return 0;
}
//max(dp[0][i],dp[1][i],dp[2][i])表示第i天后最大收益
int dp[][] = new int[3][prices.length];
// dp[0][i] 状态1:不在冷却期,持有股票
// dp[1][i] 状态2:不在冷却期,不持有股票
// dp[2][i] 状态3:冷却期
dp[0][0] = -prices[0];
for (int i = 1; i < prices.length; i++) {
//不在冷却期且持有股票,可从状态1转移(无操作),也可以从状态2转移(买入)
dp[0][i] = Math.max(dp[0][i - 1], dp[1][i - 1] - prices[i]);
//状态2可由状态2、状态3转移(两种转移方式均无操作)
dp[1][i] = Math.max(dp[1][i - 1], dp[2][i - 1]);
//状态3可由状态1转移(卖出)
dp[2][i] = dp[0][i - 1] + prices[i];
}
return Math.max(dp[0][prices.length - 1], Math.max(dp[1][prices.length - 1], dp[2][prices.length - 1]));
}
时间复杂度与空间复杂度均为O(n)。不过以上代码的空间复杂度还可以优化。因为第i天的最大收益只与dp[0][i-1],dp[1][i-1],dp[2][i-1]这三个数有关。若想进一步优化空间则将这三个数用变量储存即可。