这道题主要涉及状态转移方程,想清楚全部状态后,就能够轻松解决。
<!-- more -->git
给定一个整数数组,其中第 i 个元素表明了第 i 天的股票价格 。github
设计一个算法计算出最大利润。在知足如下约束条件下,你能够尽量地完成更多的交易(屡次买卖一支股票):算法
示例:segmentfault
输入: [1,2,3,0,2] 输出: 3 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
原题url:https://leetcode-cn.com/probl...数组
一开始我就是想着一共有几种状态,这几种状态分别能够转换为哪些状态:优化
我增长了最后终止条件:若是是最后一天,而且手上持有股票的话,必须卖出,这样能够保证最终利益最大。url
接下来看看代码:spa
class Solution { int max = 0; public int maxProfit(int[] prices) { if (prices.length == 0) { return 0; } recursiveBuy(-1, false, 0, 0, prices); return max; } public void recursiveBuy( int prePrice, boolean cooldown, int profit, int index, int[] prices) { // 若是到了最后一天,而且手上持有股票的话,必须卖掉 if (index == prices.length - 1) { if (prePrice >= 0 && !cooldown) { profit = profit + prices[index]; } max = Math.max(max, profit); return; } // 当前持有股票 if (prePrice >= 0) { // 此时能够选择不交易,或者卖掉 // 不交易 recursiveBuy(prePrice, cooldown, profit, index + 1, prices); // 卖掉 recursiveBuy(-1, true, profit + prices[index], index + 1, prices); return; } // 当前不持有股票,能够被动不交易、主动不交易、买 // 若是处于冷冻期,只能被动不交易 if (cooldown) { recursiveBuy(prePrice, false, profit, index + 1, prices); return; } // 不交易 recursiveBuy(prePrice, cooldown, profit, index + 1, prices); // 买 recursiveBuy(prices[index], cooldown, profit - prices[index], index + 1, prices); } }
报了超出时间限制
,好的,咱们想一想怎么优化。设计
上面暴力解法
之因此会超时,由于重复计算了。我一开始的想法是想着记录中间结果,但越想越复杂,忍不住看了别人的思路,真的是让我豁然开朗。那就是状态转移方程
。code
以前我上面提到的是全部状态能够变成哪些状态,但其实有些地方想的是不清楚的。咱们用箭头
链接两个状态,箭头开始
的那端表示前一天的状态,箭头终止
的那端表示当天的状态,那么其内容为:
由于买和卖只是两个操做,咱们认为只能在每一天的0点执行,当天的状态就由0点以后的状态来表示。
你可能会问,若是这样表示状态转移方程的话,那么第一天能够买入股票就无法解释了。那简单,为了配合这种特殊状况,咱们再记录一个更早一天的不持股状态,这样就能够知足了。
接下来看看代码:
class Solution { public int maxProfit(int[] prices) { if (prices.length < 2) { return 0; } // 由于每次只涉及到前一天的三个状态值,所以只要三个数字记录便可 /** * 其状态转移方程为: * "冷冻期"只能由"不持股"转换而来。 * "持股"能够由"持股"和"冷冻期"转换而来。 * "不持股"能够由"不持股"和"持股"转换而来。 */ // 定义初始状况 // 不持股 int noStock = 0; // 持股 int hasStock = -prices[0]; // 冷冻期 int cooldown = 0; // 上一次的不持股 int beforeNoStock = 0; for (int i = 1; i < prices.length; i++) { // "不持股"能够由"不持股"和"持股"转换而来。 noStock = Math.max(beforeNoStock, hasStock + prices[i]); // "持股"能够由"持股"和"冷冻期"转换而来。 hasStock = Math.max(hasStock, cooldown - prices[i]); // "冷冻期"只能由"不持股"转换而来。 cooldown = beforeNoStock; // 更新一下"上一次的不持股"状态 beforeNoStock = noStock; } return Math.max(noStock, cooldown); } }
提交OK。
其实从上面的分析,你隐约能够察觉到,"冷冻期"就是一种特殊的"不持股"状态。根据上面的结论,当你想买股票时,要求的是必须连续两天"不持股",这点你想通了吗?可能也正由于这一点,咱们在上面的代码中才须要记录"上一次的不持股"状态。
既然这样,咱们干脆就简化为持股
状态和不持股
状态两种,其状态转移方程能够描述为:
持股
状态能够由本身,或者连续两天为不持股
状态,今天买了股票,转移而来。不持股
状态能够由本身,或者前一天为持股
状态,今天卖了股票,转移而来。由于咱们记录的是每一天状态所对应的收入,那么所谓的连续两天为不持股状态
,就是至关于从两天前收入不变。
接下来看看代码:
class Solution { public int maxProfit(int[] prices) { if (prices.length < 2) { return 0; } // 此次只有两个状态,但须要记录两天前的不持股收入 // 定义初始状况 // 不持股 int noStock = 0; // 持股 int hasStock = -prices[0]; // 上一次的不持股 int beforeNoStock = 0, temp; for (int i = 1; i < prices.length; i++) { // 记录一下"两天前的不持股"收入 temp = noStock; // "不持股"能够由"不持股"和"持股"转换而来。 noStock = Math.max(noStock, hasStock + prices[i]); // "持股"能够由"持股"和"冷冻期"转换而来。 hasStock = Math.max(hasStock, beforeNoStock - prices[i]); // 更新一下"上一次的不持股"状态 beforeNoStock = temp; } // 最大值必定是最后一天不持股的状况 return noStock; } }
提交OK。
以上就是这道题目个人解答过程了,不知道你们是否理解了。状态转移应该仍是很经典的方法,主要在因而否能够想出全部状态及其转化关系。
有兴趣的话能够访问个人博客或者关注个人公众号、头条号,说不定会有意外的惊喜。
公众号:健程之道