881. Boats to Save People
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
- You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
- After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example1
Input:
[1,2,3,0,2]
Output:
3
Explanation:
transactions = [buy, sell, cooldown, buy, sell]
思路
利用动态规划算法(Dynamic Programming Algorithm)解决问题的关键是如何正确定义状态空间,以及状态转移方程。关于动态规划,最著名的问题应该是0/1背包问题了,它有许多变种,变化多样,是每个学习动态规划的人都会接触到的经典问题。
因为这样,我对0/1背包问题留下的印象特别深刻,在做这道题目的时候受到了一些影响,导致思考的方向错了。状态空间的定义显而易见地应该按天数划分,每一天是一个状态,一开始我假定购买是0
操作,出售是1
操作,每天的新状态由前一天的状态来决定今天是购买还是操作。按着这样的思路进行下去很快就会发现问题了,除了购买和出售两个操作,还有第三个操作就是什么都不干,一次交易的购买和出售之间可能间隔数天,所以我只能调整自己的思考方向了。
稍加思考就会发现,假如我这一次是购买的操作,取得的最大收益,取决于上一次交易的出售操作的最大收益减去这一次购买花掉的钱,如果这一次是出售的操作,那么最大收益就是之前购买操作的最大收益加上这一次出售赚到的钱。
我们不妨把状态空间划分成两部分,如果这一次是购买操作,最大收益是多少,出售操作的最大收益又是多少,两部分独立,互不影响。这样我们就可以得到下面的状态转移方程了。
购买的最大收益的状态转移方程:
在之前的状态中寻找出售的最大收益的状态,减去这次购买的损失,求得购买的最大收益
因为有个特殊要求是出售后第二天不能购买,所以是 j < i - 1
for (int i = 1; i < n; i++)
for (int j = 0; j < i - 1; j++)
buy[i] = Maxmum(sell[j] - prices[i])
出售的最大收益的状态转移方程:
在之前的状态中寻找购买的最大收益的状态,加上这次出售的收入,求得出售的最大收益
for (int i = 1; i < n; i++)
for (int j = 0; j < i; j++)
sell[i] = Maxmum(buy[j] + prices[i])
最后我们只要在出售的状态空间里求出最大值,就是这道题目的答案了。至于为什么最大值一定在出售的状态空间里,稍加思考的话很容易明白的,在这里我就不多加赘述了。这道题有一个巨坑就是函数传进来的表示每天股票价格的prices
数组,竟然可以是空的!没注意到的话可能要卡在runtime error
错误了,为什么会是空的啊,股市不开市吗?真的是迷的要死。
动态规划的题目真的很烧脑啊,实际代码可能就是几个循环加上几个判断赋值语句而已,但要把这个状态转移方程找出来不会很容易啊,要充分读懂题目,弄明白几个限制变量之间的关系。还是那句话吧,熟能生巧,解题速度的提升有赖于平时的反复练习,不能掉以轻心。
代码
class Solution {
public:
int max(const int& num1, const int& num2)
{
if (num1 > num2) return num1;
else return num2;
}
int maxProfit(vector<int>& prices)
{
if (prices.size() == 0) return 0;
int n = prices.size();
int buy[n];
int sell[n];
//第一天只能购买,不能出售
buy[0] = -prices[0];
sell[0] = 0;
for (int i = 1; i < n; i++)
{
buy[i] = -prices[i];
for (int j = 0; j < i - 1; j++)
buy[i] = max(buy[i], sell[j] - prices[i]);
sell[i] = -32768;
for (int j = 0; j < i; j++)
sell[i] = max(sell[i], buy[j] + prices[i]);
}
int ans = -32768;
for (int i = 0; i < n; i++)
ans = max(ans, sell[i]);
return ans;
}
};