LeetCode 309 Best Time to Buy and Sell Stock with Cooldown
题意
给出一串数组prices,数组中第i
个元素代表第i
天股票的价格,每天你可以选择购入一张股票或者售出已有股票,但有如下要求:
- 你不能同时参与多个交易,即当你手中有一张股票时你只能在卖出之后才能再次购入股票
- 售出股票后你必须等待一天才能继续购入
设计一个算法使受益最大。
思路
每天可选择购入或售出,尽管有限制条件,但仍然是阶段决策任务,可以采用动态规划。不妨假设取得最大收益p
时第n
天售出了股票,第n-1
天购入了股票,那么可以肯定的是,第n-1
天在购入股票(或者说拥有一张股票)的情况下最大收益一定是p-price[n]
,即最优解可以由子问题的最优解得到,也就是说可以通过动态规划的方式计算最大收益,那么我们来定义子结构 - 状态数组。
一种方法是定义(也是我最初的思路)
dp[i][3]=第i天 购入/售出/无操作 时前i天利益最大值
但是这种方法无法区分既不购入也不售出(无操作)
与冷却时间
这两种情况,因为处于冷却时间时也是无操作
。所以应当以拥有的股票数定义:
dp[i][3]=第i天拥有 无股票无操作/售出股票/持有一张股票 股票时前i天利益最大值
实际上,第i
天在执行完对应的操作后,前两种情况股票数为0,第三种情况下股票数为1。
状态转移方程:
dp[i][无股票无操作] = max(dp[i - 1][无股票无操作], dp[i - 1][售出股票]);
dp[i][售出股票] = dp[i - 1][有一张股票] + prices[i];
dp[i][有一张股票] = max(dp[i - 1][无股票无操作] - prices[i], dp[i - 1][有一张股票]);
这样即可通过遍历得到最后一天三种情况下的收益。
P.S. 因为dp[i]
只与dp[i-1]
有关,因此可以压缩为dp[2][3]
,通过0和1表示i-1
。
代码
class Solution {
public:
int maxProfit(vector<int> &prices) {
const int n = prices.size();
if (n <= 0)
return 0;
int dp[n + 1][3] = {};
dp[1][2] = -prices[0];
for (int i = 2; i <= n; i++) {
int pi = i - 1;
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][2] + prices[pi];
dp[i][2] = max(dp[i - 1][0] - prices[pi], dp[i - 1][2]);
}
int res = 0;
for (int i = 0; i < 3; i++)
res = max(res, dp[n][i]);
return res;
}
};
总结
多段决策任务往往可以通过动态规划的方法解决,但子结构与状态转移方程是解题的关键。