leetcode - 動(dòng)態(tài)規(guī)劃 - Part3

121. 買(mǎi)賣股票的最佳時(shí)機(jī)

題目描述

給定一個(gè)數(shù)組腐缤,它的第 i 個(gè)元素是一支給定股票第 i 天的價(jià)格舞蔽。 
如果你最多只允許完成一筆交易(即買(mǎi)入和賣出一支股票一次)婉徘,設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)钻趋。 
注意:你不能在買(mǎi)入股票前賣出股票邻寿。 

示例 1: 
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價(jià)格 = 1)的時(shí)候買(mǎi)入渤弛,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出己肮,最大利潤(rùn) = 6-1 = 5 主穗。
注意利潤(rùn)不能是 7-1 = 6, 因?yàn)橘u出價(jià)格需要大于買(mǎi)入價(jià)格圃伶;同時(shí)堤如,你不能在買(mǎi)入前賣出股票。

示例 2: 
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒(méi)有交易完成, 所以最大利潤(rùn)為 0窒朋。

Related Topics 數(shù)組 動(dòng)態(tài)規(guī)劃

由于最多只交易一次搀罢,我們只需要找到當(dāng)前天以前的股票最低價(jià),也就能計(jì)算出當(dāng)前天的最大利潤(rùn)侥猩。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        low = prices[0]
        profit = 0
        for i in range(1, n):
            low = min(low, prices[i])
            profit = max(profit, prices[i] - low)
        return profit

下面我們嘗試?yán)脛?dòng)態(tài)規(guī)劃算法進(jìn)行求解榔至。

分析思路:
動(dòng)態(tài)規(guī)劃也就是窮舉狀態(tài)。對(duì)于每一天來(lái)說(shuō)欺劳,我們有以下3種選擇:不交易唧取、買(mǎi)入、賣出划提。但是這3種狀態(tài)并不是自由選擇的枫弟,買(mǎi)入需要在賣出以后(第一次買(mǎi)入除外),賣出需要在買(mǎi)入以后鹏往。另外還需要考慮交易次數(shù)的限制淡诗。所以綜合起來(lái),我們的狀態(tài)有3類:天數(shù)掸犬、交易次數(shù)袜漩、持股狀態(tài)。
所以我們可以定義狀態(tài)數(shù)組為 dp[i][j][k]湾碎,表示第 i 天最多交易 j 次且持股狀態(tài)為 k{0,1} 的狀態(tài)下能獲得的最大利潤(rùn)宙攻,其中 k=0 表示不持股,k=1表示持股介褥。

  • dp[i][j][0] 如何轉(zhuǎn)移座掘?
    若不交易,則 dp[i][j][0] = dp[i-1][j][0]
    賣出后的不持股柔滔,則 dp[i][j][0] = dp[i-1][j][1]+prices[i]
    所以 dp[i][j][0] = max( dp[i-1][j][0], dp[i-1][j][1]+prices[i])

  • dp[i][j][1] 如何轉(zhuǎn)移溢陪?
    若不交易,則 dp[i][j][1] = dp[i-1][j][1]
    買(mǎi)入后的持股睛廊,則 dp[i][j][1] = dp[i-1][j][0]-prices[i]
    所以 dp[i][j][1] = max( dp[i-1][j][1], dp[i-1][j-1][0]-prices[i])

  • base case
    第 0 天不持股形真,最大利潤(rùn)為0
    dp[0][j][0] = 0
    第 0 天持股,最大利潤(rùn)為-prices[0]
    dp[0][j][1] = -prices[0]
    交易次數(shù)為 0超全,不持股狀態(tài)下利潤(rùn)為0咆霜,不可能為持股狀態(tài)邓馒,故設(shè)置為負(fù)無(wú)窮。
    dp[i][0][0] = 0
    dp[i][0][1] = float('-inf')

  • 輸出
    即第 n-1 天最大交易次數(shù)下且不持股狀態(tài)下的最大利潤(rùn)蛾坯。

有了這套思路光酣,對(duì)于買(mǎi)賣股票類問(wèn)題,我們均可以套用脉课。
回到題目救军,由于最多交易一次,即 j=1倘零,此時(shí)狀態(tài)轉(zhuǎn)移方程為:
\left\{\begin{matrix} dp[i][1][0] = max( dp[i-1][1][0], dp[i-1][1][1]+prices[i]) \\ dp[i][1][1] = max( dp[i-1][1][1], dp[i-1][0][0]-prices[i]) \\ = max( dp[i-1][1][1], -prices[i]) \end{matrix}\right.
由于交易次數(shù)這個(gè)維度均為1唱遭,所以可以省略,最后的狀態(tài)轉(zhuǎn)移方程為:
\left\{\begin{matrix} dp[i][0] = max( dp[i-1][0], dp[i-1][1]+prices[i]) \\ dp[i][1] = max( dp[i-1][1], -prices[i]) \end{matrix}\right.

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        dp = [[0, 0] for _ in range(n)]
        # 初始化
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        # 遍歷狀態(tài)
        for i in range(1, n):
                dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
                dp[i][1] = max(dp[i-1][1], -prices[i])
        # print(dp)
        return dp[n-1][0]

由于 dp[i] 只與 dp[i-1] 有關(guān)呈驶,進(jìn)行狀態(tài)壓縮:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        sale = 0
        buy = -prices[0]
        for i in range(1, n):
            sale = max(sale, buy + prices[i])
            buy = max(buy, -prices[i])
        return sale

122. 買(mǎi)賣股票的最佳時(shí)機(jī) II

題目描述

給定一個(gè)數(shù)組胆萧,它的第 i 個(gè)元素是一支給定股票第 i 天的價(jià)格。 
設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)俐东。你可以盡可能地完成更多的交易(多次買(mǎi)賣一支股票)跌穗。 
注意:你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買(mǎi)前出售掉之前的股票)。 

示例 1: 
輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價(jià)格 = 1)的時(shí)候買(mǎi)入虏辫,在第 3 天(股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 5-1 = 4 蚌吸。
隨后,在第 4 天(股票價(jià)格 = 3)的時(shí)候買(mǎi)入砌庄,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 6-3 = 3 羹唠。

示例 2: 
輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價(jià)格 = 1)的時(shí)候買(mǎi)入,在第 5 天 (股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 5-1 = 4 娄昆。
注意你不能在第 1 天和第 2 天接連購(gòu)買(mǎi)股票佩微,之后再將它們賣出。
因?yàn)檫@樣屬于同時(shí)參與了多筆交易萌焰,你必須在再次購(gòu)買(mǎi)前出售掉之前的股票哺眯。

示例 3: 
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒(méi)有交易完成, 所以最大利潤(rùn)為 0。 

提示: 
1 <= prices.length <= 3 * 10 ^ 4 
0 <= prices[i] <= 10 ^ 4 

Related Topics 貪心算法 數(shù)組 

此題與上一題的不同之處在于交易次數(shù)無(wú)限制扒俯。還是套用我們上面的分析思路奶卓,狀態(tài)轉(zhuǎn)移方程為
\left\{\begin{matrix} dp[i][j][0] = max( dp[i-1][j][0], dp[i-1][j][1]+prices[i]) \\ dp[i][j][1] = max( dp[i-1][j][1], dp[i-1][j-1][0]-prices[i]) \end{matrix}\right.
交易次數(shù)無(wú)限制,我們也可以理解為 j=+infinity撼玄,此時(shí) j-1j 可以認(rèn)為相等夺姑,即第二維同樣可以省略,所以狀態(tài)轉(zhuǎn)移方程可寫(xiě)成:
\left\{\begin{matrix} dp[i][0] = max( dp[i-1][0], dp[i-1][1]+prices[i]) \\ dp[i][1] = max( dp[i-1][1], dp[i-1][0]-prices[i]) \end{matrix}\right.

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # dp[i][k] 表示第i天持股狀態(tài)為k{0: 不持股, 1: 持股}的最大利潤(rùn)
        n = len(prices)
        if n <= 1:
            return 0
        dp = [[0, 0] for _ in range(n)]
        dp[0][1] = -prices[0]
        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
            dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
        # print(dp)
        return dp[n-1][0]

同樣進(jìn)行進(jìn)行狀態(tài)壓縮:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # dp[i][k] 表示第i天持股狀態(tài)為k{0: 不持股, 1: 持股}的最大利潤(rùn)
        n = len(prices)
        if n <= 1:
            return 0
        sale = 0
        buy = -prices[0]
        for i in range(1, n):
            sale = max(sale, buy+prices[i])
            buy = max(buy, sale-prices[i])
        return sale

123. 買(mǎi)賣股票的最佳時(shí)機(jī) III

題目描述

給定一個(gè)數(shù)組掌猛,它的第 i 個(gè)元素是一支給定的股票在第 i 天的價(jià)格盏浙。 
設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)。你最多可以完成 兩筆 交易。 
注意: 你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買(mǎi)前出售掉之前的股票)废膘。 

示例 1: 
輸入: [3,3,5,0,0,3,1,4]
輸出: 6
解釋: 在第 4 天(股票價(jià)格 = 0)的時(shí)候買(mǎi)入辣往,在第 6 天(股票價(jià)格 = 3)的時(shí)候賣出,這筆交易所能獲得利潤(rùn) = 3-0 = 3 殖卑。
隨后,在第 7 天(股票價(jià)格 = 1)的時(shí)候買(mǎi)入坊萝,在第 8 天 (股票價(jià)格 = 4)的時(shí)候賣出孵稽,這筆交易所能獲得利潤(rùn) = 4-1 = 3 。 

示例 2: 
輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價(jià)格 = 1)的時(shí)候買(mǎi)入十偶,在第 5 天 (股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 5-1 = 4 菩鲜。   
注意你不能在第 1 天和第 2 天接連購(gòu)買(mǎi)股票,之后再將它們賣出惦积。   
因?yàn)檫@樣屬于同時(shí)參與了多筆交易接校,你必須在再次購(gòu)買(mǎi)前出售掉之前的股票。

示例 3: 
輸入: [7,6,4,3,1] 
輸出: 0 
解釋: 在這個(gè)情況下, 沒(méi)有交易完成, 所以最大利潤(rùn)為 0狮崩。 
Related Topics 數(shù)組 動(dòng)態(tài)規(guī)劃 

現(xiàn)在交易次數(shù)被限制為 k=2蛛勉,我們可以列舉第二維的狀態(tài):

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        dp = [[[0, 0] for _ in range(3)] for _ in range(n)]
        # 初始化
        dp[0][0][1] = float('-inf')
        dp[0][1][1] = -prices[0]
        dp[0][2][1] = -prices[0]
        for i in range(1, n):
            # dp[i][0][0] = 0
            # dp[i][0][1] = float('-inf')
            dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][1][1]+prices[i])
            dp[i][1][1] = max(dp[i-1][1][1], -prices[i])
            dp[i][2][0] = max(dp[i-1][2][0], dp[i-1][2][1]+prices[i])
            dp[i][2][1] = max(dp[i-1][2][1], dp[i-1][1][0]-prices[i])
        return dp[n-1][2][0]

同樣進(jìn)行狀態(tài)壓縮:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        first_buy = -prices[0]
        first_sale = float('-inf')
        second_buy = float('-inf')
        second_sale = float('-inf')
        for i in range(1, n):
            first_buy = max(first_buy, -prices[i])
            first_sale = max(first_sale, first_buy+prices[i])
            second_buy = max(second_buy, first_sale-prices[i])
            second_sale = max(second_sale, second_buy+prices[i])
        return second_sale

188. 買(mǎi)賣股票的最佳時(shí)機(jī) IV

題目要求最多交易次數(shù)為k,針對(duì)前面幾題的分析睦柴,我們可以對(duì)k進(jìn)行分段討論诽凌。

309. 最佳買(mǎi)賣股票時(shí)機(jī)含冷凍期

題目描述

給定一個(gè)整數(shù)數(shù)組,其中第 i 個(gè)元素代表了第 i 天的股票價(jià)格 坦敌。 

設(shè)計(jì)一個(gè)算法計(jì)算出最大利潤(rùn)侣诵。在滿足以下約束條件下,你可以盡可能地完成更多的交易(多次買(mǎi)賣一支股票): 
你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買(mǎi)前出售掉之前的股票)狱窘。 
賣出股票后杜顺,你無(wú)法在第二天買(mǎi)入股票 (即冷凍期為 1 天)。 

示例: 
輸入: [1,2,3,0,2]
輸出: 3 
解釋: 對(duì)應(yīng)的交易狀態(tài)為: [買(mǎi)入, 賣出, 冷凍期, 買(mǎi)入, 賣出] 
Related Topics 動(dòng)態(tài)規(guī)劃 

還是套用前面的思路

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n <= 1:
            return 0
        dp = [[0, 0] for _ in range(n)]
        # 初始化
        dp[0][1] = -prices[0]
        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
            if i == 1:
                dp[i][1] = max(dp[i-1][1], -prices[i])
            else:
                dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i])
        return dp[n-1][0]

714. 買(mǎi)賣股票的最佳時(shí)機(jī)含手續(xù)費(fèi)

參考

  1. labuladong 的解題蘸炸。(放鏈接一直被吞)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末躬络,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搭儒,更是在濱河造成了極大的恐慌洗鸵,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仗嗦,死亡現(xiàn)場(chǎng)離奇詭異膘滨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)稀拐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)火邓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事铲咨《愀欤” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵纤勒,是天一觀的道長(zhǎng)坯苹。 經(jīng)常有香客問(wèn)我,道長(zhǎng)摇天,這世上最難降的妖魔是什么粹湃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮泉坐,結(jié)果婚禮上为鳄,老公的妹妹穿的比我還像新娘。我一直安慰自己腕让,他們只是感情好孤钦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纯丸,像睡著了一般偏形。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上觉鼻,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天壳猜,我揣著相機(jī)與錄音,去河邊找鬼滑凉。 笑死统扳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畅姊。 我是一名探鬼主播咒钟,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼若未!你這毒婦竟也來(lái)了朱嘴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粗合,失蹤者是張志新(化名)和其女友劉穎萍嬉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體隙疚,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壤追,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了供屉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片行冰。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡溺蕉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悼做,到底是詐尸還是另有隱情疯特,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布肛走,位于F島的核電站漓雅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏朽色。R本人自食惡果不足惜邻吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵搁。 院中可真熱鬧,春花似錦往踢、人聲如沸腾誉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)利职。三九已至,卻和暖如春瘦癌,著一層夾襖步出監(jiān)牢的瞬間猪贪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工讯私, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留热押,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓斤寇,卻偏偏與公主長(zhǎng)得像桶癣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娘锁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345