這道題主要涉及狀態轉移方程,想清楚全部狀態後,就能夠輕鬆解決。
<!-- 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。
以上就是這道題目個人解答過程了,不知道你們是否理解了。狀態轉移應該仍是很經典的方法,主要在因而否能夠想出全部狀態及其轉化關係。
有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。
公衆號:健程之道