【labuladong的算法小抄】股票買賣問題

用狀態(tài)機(jī)的技巧(不要害怕敷燎,其實(shí)就是DP table)來解決股票買賣問題,可以全部提交通過兰绣。

leetcode 買賣股票的最佳時(shí)機(jī)的 6 道題是有共性的搪柑,看下 IV 題吮蛹,是最泛化的形式,其他的問題都是這個(gè)形式的簡化:

第 I 題是只進(jìn)行一次交易拌屏, 相當(dāng)于 k = 1;

第 II 題是不限交易次數(shù)潮针,相當(dāng)于 k = +INF;

第 III 題是只進(jìn)行2次交易倚喂,相當(dāng)于 k = 2每篷;

剩下兩道題也是不限次數(shù)瓣戚,但是加了交易?「冷凍期」和「?續(xù)費(fèi)」的額外條件,其實(shí)就是第二題的變種焦读,都很容易處理子库。


一、窮舉框架

如何窮舉矗晃?窮舉和遞歸的思想不太一樣仑嗅。遞歸其實(shí)是符合我們思考的邏輯的:一步步推進(jìn),遇到無法解決的就丟給遞歸张症,一不小心就做出來仓技,可讀性還很好。缺點(diǎn)就是一旦出錯(cuò)俗他,也不容易找到錯(cuò)誤出現(xiàn)的原因脖捻。

這里我們利用「狀態(tài)」進(jìn)?窮舉。具體到每一天兆衅,看看總共有幾種可能的「狀態(tài)」地沮,再找出每個(gè)「狀態(tài)」對(duì)應(yīng)的選擇,窮舉的目的是根據(jù)對(duì)應(yīng)的「選擇」更新狀態(tài)羡亩。

比如這個(gè)問題摩疑,每天都有三種「選擇」:買入(buy)、賣出(sell)畏铆、無操作(rest)雷袋。但這三種選擇不是相互獨(dú)立的:sell 必須在 buy 之后。并且操作還應(yīng)該分兩種狀態(tài)及志,一種是未持有(0)的操作,一種是已持有(1)后的操作寨腔。并且速侈,剩余可交易次數(shù) k 還限制 buy 只能在 k>0 的前提下操作。

這個(gè)問題的「狀態(tài)」有三個(gè): 第?個(gè)是天數(shù)迫卢;第?個(gè)是允許交易的最?次數(shù)倚搬;第三個(gè)是當(dāng)前的持有狀態(tài)( 1 表?持有,0 表?空倉)乾蛤,然后我們用一個(gè)三維數(shù)組就可以 裝下下面這幾種狀態(tài)的全部組合:

dp[3][2][1] 的含義:今天是第三天每界,現(xiàn)在手上持有股票,最多允許2次交易家卖。

我們想求的最終答案是dp[n-1][K][0]眨层,即最后一天,最多允許K次交易上荡,最多獲得多少利潤趴樱。[0] 表示最終不持有股票馒闷,當(dāng)然比持有股票利潤更大。


二叁征、狀態(tài)轉(zhuǎn)移框架

我們已經(jīng)完成了「狀態(tài)」的窮舉纳账,現(xiàn)在思考每種「狀態(tài)」有哪些「選擇」,應(yīng)該如何更新「狀態(tài)」捺疼。

如果 buy疏虫,就要從利潤中減去 prices[i],如果 sell啤呼,就要給利潤增加 prices[i]卧秘。并且注意 k 的限制,我們?cè)谶x擇 buy 的時(shí)候媳友,把 k 減小 1斯议,相當(dāng)于剩下可用的交易次數(shù)少 1。

(如果 k 代表當(dāng)前允許交易次數(shù)的話醇锚,隨著買入操作哼御,k應(yīng)該減小,是買入導(dǎo)致了k減小焊唬,所以應(yīng)該是 dp[i][k-1][l] = dp[i-1][k][0] - prices[i]恋昼;而此處 k 代表當(dāng)前已進(jìn)行的交易次數(shù),所以隨著買入操作赶促,k會(huì)增大液肌。兩種理解都可以,就是狀態(tài)轉(zhuǎn)移方程和base case 里面涉及k的對(duì)應(yīng)修改一下鸥滨。作者這里的解釋和他給的狀態(tài)轉(zhuǎn)移方程對(duì)不上嗦哆,讀者不必糾結(jié),按這兩種k的理解寫出自己的狀態(tài)轉(zhuǎn)移方程和base case 即可)

這里需要做出更詳細(xì)的解釋婿滓。假設(shè) i = 5老速,那么我們著手畫出它的狀態(tài)轉(zhuǎn)移圖:

好吧,畫不下去了凸主。

原因是:第4天到底是賣出橘券、買入還是休息,取決于目前是否已經(jīng)持有股票卿吐,所以這里分化出了兩棵樹:一棵是當(dāng)前持有旁舰,一棵是當(dāng)前空倉。

而第4天是否持有股票又取決于第3天的持有狀態(tài)及操作嗡官。

當(dāng)沒有買賣次數(shù)的限制條件時(shí)箭窜,上圖的每條路徑都可以走,但當(dāng)有了買賣次數(shù)限制條件 k 時(shí)衍腥,每做 1 次買賣(假設(shè)買時(shí)記 k-1绽快,賣時(shí)k不變)芥丧,k 將減少 1,直到 k=0 時(shí)坊罢,允許的可買賣次數(shù)用盡续担,后面的買路徑就不能再走了,如下圖所示活孩,假如走了綠色的路徑物遇,那紅色的路徑就因?yàn)閗=0不能再買入而走不通了:

(注意,這里的 k 表示當(dāng)前允許的最大買賣次數(shù))

那么 k 的加入相當(dāng)于幫我們剪掉了某些路徑憾儒,一旦 k = 0询兴,就不能再進(jìn)行買入操作。所以我們每次操作選擇后起趾,還需要維護(hù) k 的狀態(tài)诗舰,但選擇不同的路徑,會(huì)獲得不同的 k 值训裆,也就是 :

dp[0][0]? 第0天眶根,空倉,對(duì)應(yīng)一個(gè) k值為1,

dp[1][0] 第1天边琉,空倉属百,對(duì)應(yīng)一個(gè)k值為1,

dp[1][1] 第1天变姨,持有族扰,對(duì)應(yīng)一個(gè)k值為0,

dp[2][0] 第2天定欧,空倉渔呵,這里就有兩種可能,

????????如果dp[2][0] = dp[1][1] + prices[1]砍鸠,說明第一天持有并賣出扩氢,k = 0

? ? ? ? 如果dp[2][0] = dp[1][0],說明第一天空倉并休息睦番,k=1

所以 k 與 dp[i][s] 是多對(duì)一關(guān)系类茂,且范圍在 [0,k] 波動(dòng)耍属,所以這些可能性需要新開一個(gè)維度來記錄托嚣,現(xiàn)在dp[i][s] 擴(kuò)展到了 dp[i][k][s],那么 dp[i][0][s] 代表 k=0時(shí)的dp[i][s]值厚骗,而 dp[i][1][s] 代表 k=1時(shí)的值示启。這里不太容易理解,也就是數(shù)組下標(biāo)對(duì)應(yīng)了 k 的取值领舰,剛好數(shù)組下標(biāo)不能小于0夫嗓。

現(xiàn)在迟螺,我們已經(jīng)完成了動(dòng)態(tài)規(guī)劃中最困難的一步:狀態(tài)轉(zhuǎn)移方程。下面就要定義base case了:

(作者這里的解釋不對(duì)舍咖, k=0意味著不允許交易矩父,但不是利潤為0的原因,雖然不允許交易排霉,但可以繼承前面幾天的利潤對(duì)吧窍株?這里利潤為0是因?yàn)椋x的 k=0 其實(shí)是當(dāng)前已交易次數(shù)攻柠,最大交易次數(shù)是1球订,已交易次數(shù)為0,說明沒有交易過 瑰钮,利潤當(dāng)然是0冒滩;而dp[i][0][1] = -infinity 也不是因?yàn)椴辉试S交易的情況下不可能持有股票,因?yàn)榭梢岳^承過去買入的股票浪谴,雖然不能買入但一直沒有賣出开睡,也是持有股票的對(duì)吧?作者定義的 k=0其實(shí)是當(dāng)前已交易次數(shù)较店,當(dāng)前已交易次數(shù)為0說明不可能已經(jīng)買入士八,所以不可能持有。)

把上面的狀態(tài)轉(zhuǎn)移方程總結(jié)一下:


三梁呈、題目實(shí)戰(zhàn)

? ? 1. 第 I 題婚度,k = 1

k=1 即只允許買賣一次,狀態(tài)轉(zhuǎn)移方程如下(注意作者的 k 表示當(dāng)前已交易次數(shù)):

(第 i 天官卡,空倉)的狀態(tài)蝗茁,可以從(第 i-1 天,空倉寻咒,第 i 天休息)哮翘、(第 i-1天,持有,第i 天賣出)這兩種途徑達(dá)到。

(第 i 天儿捧,持有)的狀態(tài)浊闪,可以從(第i-1天,持有狞悲,第 i 天休息)、(第 i-1 天,空倉员凝,第 i 天買入)這兩種途徑達(dá)到。

化簡后發(fā)現(xiàn) k 都是 1奋献,即對(duì)狀態(tài)轉(zhuǎn)移方程沒有影響健霹,可以進(jìn)一步簡化:

寫出代碼:

dp[n-1][0] 就是最后一天旺上,空倉時(shí)的總收益。

但 i=0 時(shí)糖埋, dp[i-1] 是越界的宣吱,所以還要對(duì)base case 進(jìn)行處理。

注意今天的狀態(tài)只依賴于昨天的狀態(tài)瞳别,所以用不上整個(gè)dp 數(shù)組凌节,只需要兩個(gè)變量來更新每天的持有、空倉兩個(gè)狀態(tài)即可洒试。


? ? 2. 第 II 題倍奢,k = +INF

·如果 k 為正無窮,可以認(rèn)為 k 和 k-1是一樣的垒棋,可以這樣改寫狀態(tài)轉(zhuǎn)移方程:

代碼如下:


? ? 3. 最佳買賣股票時(shí)機(jī)含冷凍期?卒煞,k = + INF with cooldown

每次賣出后,要等一天才能繼續(xù)交易叼架,只要把這個(gè)特點(diǎn)融入上一題的狀態(tài)轉(zhuǎn)移方程即可:

代碼:


? ? 4. 最佳買賣股票時(shí)機(jī)含手續(xù)費(fèi)畔裕,k = + INF with fee

每次交易要支付手續(xù)費(fèi),只要把手續(xù)費(fèi)從利潤中減去即可乖订。

代碼:


? ? 5. 第 III 題扮饶,k = 2

由于沒有消除 k的影響,所以必須對(duì) k 進(jìn)行窮舉(循環(huán)):

其實(shí)這里有點(diǎn)問題乍构,max_k 不一定 小于 n甜无,假如只有2天,max_k = 3哥遮,這樣會(huì)產(chǎn)生非常多的無效計(jì)算:

當(dāng)然這種窮舉不是每個(gè) dp[i][k][s] 都有意義岂丘,比如 dp[0][1][s] 代表第0天只允許再交易1次,但這樣的節(jié)點(diǎn)是不可能存在的眠饮,一開始允許的交易最大次數(shù)是2次奥帘;再者,dp[1][0][s] 也是不可能存在的仪召,因?yàn)閯傔^去一天寨蹋,不可能交易次數(shù)就減少了2。(如下圖示 i扔茅、k與作者的設(shè)定不同已旧,i =0 表示還未開始,i=1才表示第一天咖摹,k=2表示還未買入评姨,k=0表示已買過2次难述,允許購買次數(shù)為0)

由于 k 的取值范圍比較小萤晴,這里還可以直接把 k=1和 k=2的情況全部列出來:


? ? 6. 第 IV 題吐句,K = 給定

有了 K=2 的鋪墊,這題解法應(yīng)該和上一題沒什么區(qū)別店读,但會(huì)超內(nèi)存嗦枢。其實(shí)有效的 K 不應(yīng)該超過 n/2,更進(jìn)一步地屯断,在窮舉過程中文虏,如果 k 表示已交易次數(shù), k應(yīng)該 不大于 i/2殖演,因?yàn)槊績商熳疃嘟灰滓淮窝趺兀蝗绻?k表示當(dāng)前最大可交易數(shù),K - k < i/2趴久。

這里代碼只處理了宏觀的情況丸相,即給定的 K 不應(yīng)超過 n/2 的情況,直接當(dāng)成K=INF 來調(diào)用了前面的代碼:


四彼棍、總結(jié)

用一個(gè)狀態(tài)轉(zhuǎn)移方程完成了 6 道股票買賣問題灭忠,其實(shí)不難,但這已經(jīng)屬于動(dòng)態(tài)規(guī)劃問題中較困難的了座硕,我們發(fā)現(xiàn)了三個(gè)狀態(tài)弛作,使用了一個(gè)三維數(shù)組,可以說是三維DP問題了华匾,但解法無非還是窮舉 + 更新映琳。

關(guān)鍵就在于列出所有可能的「狀態(tài)」,然后想想怎么窮舉更新這些「狀態(tài)」蜘拉。一般用一個(gè)多維 dp 數(shù)組存儲(chǔ)這些狀態(tài)刊头,從 base case 開始向后推進(jìn),推進(jìn)道最后的狀態(tài)诸尽,就是我們想要的答案原杂。

作者這part寫得不是很好,他的思路和如下差不多您机,但沒寫清楚穿肄,有疑惑的朋友可以看下面的(leetcode @千萬利器莫過于信念):

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市际看,隨后出現(xiàn)的幾起案子咸产,更是在濱河造成了極大的恐慌,老刑警劉巖仲闽,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脑溢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屑彻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門验庙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人社牲,你說我怎么就攤上這事粪薛。” “怎么了搏恤?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵违寿,是天一觀的道長。 經(jīng)常有香客問我熟空,道長藤巢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任息罗,我火速辦了婚禮菌瘪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阱当。我一直安慰自己俏扩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布弊添。 她就那樣靜靜地躺著录淡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪油坝。 梳的紋絲不亂的頭發(fā)上嫉戚,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音澈圈,去河邊找鬼彬檀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞬女,可吹牛的內(nèi)容都是我干的窍帝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼诽偷,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼坤学!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起报慕,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤深浮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后眠冈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體飞苇,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了布卡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雨让。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖羽利,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刊懈,我是刑警寧澤这弧,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站虚汛,受9級(jí)特大地震影響匾浪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卷哩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一蛋辈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧将谊,春花似錦冷溶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至栋齿,卻和暖如春苗胀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓦堵。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工基协, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇用。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓澜驮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惋鸥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泉唁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容