leetcode-----買賣股票最佳時機1、2、3(暴力法,動態規劃)

買賣股票最佳時機1:

解法1:(暴力法)

首先,最簡單的方法,暴力法:

兩個循環,枚舉所有買股票和買股票的可能,及其他們的收益。取最大的收益。

class Solution:
    def maxProfit(self, prices) -> int:
        profit = 0
        for i in range(len(prices)):
            for j in range(i+1,len(prices)):
                profit = max(profit,prices[j]-prices[i])
        print(profit)

l=[6,2,8,1,5]
ss = Solution()
ss.maxProfit(l)

時間複雜度:O(N^2),N爲價格數組的長度。

空間複雜度:O(1)

 

解法二:動態規劃

若含有從列表的左到右掃過來的特徵的,可以用動態規劃。

如下面代碼的 min_prices爲例,意思是在遍歷列表時,一邊遍歷一邊找最小值,那麼當前最小值等min(之前的最小值,當前值),同樣 max_profit也是同樣原理。因此可以用動態規劃實現。因爲動態規劃的意思就是 當前的狀態可以由之前的狀態和現在狀態決定。

class Solution:
    def maxProfit(self, prices) -> int:
        if prices == []: return 0
        n = len(prices)
        max_profit = 0
        min_prices = prices[0]
        for i in range(n):
            min_prices = min(min_prices,prices[i])
            max_profit = max(max_profit,prices[i]-min_prices)
        return max_profit

l=[6,2,8,1,5]
ss = Solution()
ss.maxProfit(l)

時間複雜度:O(N)

空間複雜度:O(1)

==============================================================================

買賣股票最佳時機2:

思路:由於可以做多次交易,所以相當於求多個階段的收益,如下圖,例如 8大於1,所以就開始結算之前的收益。如果8的後面是9的話,比8大,那就繼續往前,直接下一個狀態比其小,纔開始結算收益。

class Solution:
    def maxProfit(self, prices) -> int:
        if prices == []: return 0
        max_price = prices[0]
        min_prices = prices[0]
        profit = 0
        n = len(prices)
        for i in range(n):
            if max_price > prices[i]:
                profit += (max_price-min_prices)
                max_price = prices[i]
                min_prices = prices[i]
            else:
                max_price = prices[i]
                min_prices = min(min_prices, prices[i])
            if i == n-1:
                profit += (max_price - min_prices)
        return profit
l=[6,2,8,1,5]
ss = Solution()
res = ss.maxProfit(l)
print(res) #10

時間複雜度:O(N) 空間複雜度:O(1)

==============================================================================

買賣股票最佳時機3:

[動態規劃思路]:

動態規劃的思路就是 “當前的狀態由之前的狀態決定”。所以只需要設置好初始條件,就可以推出任何時刻的狀態。

而且動態規劃有一種“暴力法”的味道在裏面,因爲未經空間優化的動態規劃,是維護一個數組dp,此數組包含了所有情況。

迴歸正題:

我們已知的條件是:

1. 一共是 n 天。

2. 最多隻能交易2次。

那麼我們可以給狀態數組dp開始定義(這步我覺得是比寫代碼難的),我們把 dp 設成三維的,dp[X][Y][Z]:

第一維X:表示天數,既然是 n 天,那麼X的最大長度就是 n-1 (因爲數組是從0開始計數的)

第二維Y:表示是否持股,長度爲2,要麼是0,表示沒持股,要麼是1,表示持股。

第三維Z:表示已經交易了的次數,長度爲3,分別表示沒交易過,交易了一次,交易了兩次。

所以數組dp 記錄了每種情況下的收益。

我們的步驟是:

1.先初始化初始條件,即第0天時,各個情況的收益:

        #第一天休息
        dp[0][0][0]=0
        #第一天買入
        dp[0][1][0]=-prices[0]
        # 第一天不可能已經有賣出
        dp[0][0][1] = float('-inf')  # -inf在python中表示負無窮大
        dp[0][0][2] = float('-inf')
        #第一天不可能已經賣出
        dp[0][1][1]=float('-inf')
        dp[0][1][2]=float('-inf')

2. 根據初始條件(第一天的情況),逐步開始推導第二天、第三天.....的情況:

        for i in range(1,length):
            #未持股,未賣出過,說明從未進行過買賣
            dp[i][0][0]=0
            #未持股,賣出過1次,可能是今天賣的,可能是之前賣的
            dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
            #未持股,賣出過2次,可能是今天賣的,可能是之前賣的
            dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
            #持股,未賣出過,可能是今天買的,可能是之前買的
            dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
            #持股,賣出過1次,可能是今天買的,可能是之前買的
            dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
            #持股,賣出過2次,不可能
            dp[i][1][2]=float('-inf')

3. 最後從最後一天的 交易1次的收益 和 交易2次的收益中取最大的那個即可,當然收益必須大於0,否則最高收益是0

完整代碼:

class Solution:
    def maxProfit(self, prices):
        if prices==[]:
            return 0
        length=len(prices)
        #結束時的最高利潤=[天數][是否持有股票][賣出次數]
        dp=[ [[0,0,0],[0,0,0] ] for i in range(0,length) ]
        #第一天休息
        dp[0][0][0]=0
        #第一天買入
        dp[0][1][0]=-prices[0]
        # 第一天不可能已經有賣出
        dp[0][0][1] = float('-inf')
        dp[0][0][2] = float('-inf')
        #第一天不可能已經賣出
        dp[0][1][1]=float('-inf')
        dp[0][1][2]=float('-inf')
        for i in range(1,length):
            #未持股,未賣出過,說明從未進行過買賣
            dp[i][0][0]=0
            #未持股,賣出過1次,可能是今天賣的,可能是之前賣的
            dp[i][0][1]=max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])
            #未持股,賣出過2次,可能是今天賣的,可能是之前賣的
            dp[i][0][2]=max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])
            #持股,未賣出過,可能是今天買的,可能是之前買的
            dp[i][1][0]=max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])
            #持股,賣出過1次,可能是今天買的,可能是之前買的
            dp[i][1][1]=max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])
            #持股,賣出過2次,不可能
            dp[i][1][2]=float('-inf')
        return max(dp[length-1][0][1],dp[length-1][0][2],0)

s = Solution()
res = s.maxProfit([8,7,4,7,5,4,8,16,2])
print(res) #15

爲了理解,下面給出數組dp的內容,還是以上述代碼的例子爲例:

裏面記錄着各種情況的收益,。-inf是無窮的意思,表示不可能事件。

[
    [[0, -inf, -inf], [-8, -inf, -inf]],   #第一天
    [[0, -1, -inf], [-7, -inf, -inf]],     #第二天
    [[0, -1, -inf], [-4, -5, -inf]],     #第三天
    [[0, 3, 2], [-4, -5, -inf]], 
    [[0, 3, 2], [-4, -2, -inf]],
    [[0, 3, 2], [-4, -1, -inf]], 
    [[0, 4, 7], [-4, -1, -inf]], 
    [[0, 12, 15], [-4, -1, -inf]],
    [[0, 12, 15], [-2, 10, -inf]]        #最後一天
]

【簡化版】:

上面的版本是動態規劃的基礎版本,因爲數組dp沒優化。但是我們仔細看可以看出,數組dp基本上只用了一次,所以可以用幾個遍歷表示:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) <= 1: return 0 
        #n_01表示第n天,0表示沒持股,1表示已經交易了1次
        n_01, n_02, n_10, n_11 = float("-inf"), float("-inf"), -prices[0], float("-inf")
        for i in range(1, len(prices)):
            c_01 = max(n_01, n_10+prices[i])
            c_02 = max(n_02, n_11+prices[i])
            c_10 = max(n_10, 0 - prices[i])
            c_11 = max(n_11, n_01-prices[i])

            n_01, n_02, n_10, n_11 = c_01, c_02, c_10, c_11
        return max(n_01, n_02, 0)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章