1. 程式人生 > 其它 >經典dp--股票買賣系列系列之GO寫法

經典dp--股票買賣系列系列之GO寫法

技術標籤:Golang學習演算法學習

經典dp–股票買賣系列系列之GO寫法


一般方法求解(非DP)


121. 買賣股票的最佳時機

該題只能買賣一次,因此只要找到一個買入最低點以及對應的賣出最高點即可。因此我們可以使用一個變數minValue儲存遍歷過程中的最低點,然後在後續遍歷中不斷用price[i]減去minValue, 並取這個過程中的最大值即可。

func maxProfit(prices []int) int {
	length := len(prices)
	if length < 2 {
		return 0
	}
	minValue = prices[0]
	for
i := 1; i < length; i++ { minValue = min(minValue, prices[i]) res = max(res, prices[i] - minValue) } return res } func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b }

122. 買賣股票的最佳時機 II

這個則是可以找到一個單調上升的直線,作為買入賣出點就好了,然後將這個差值相加則是可以作為最終答案。

在這裡插入圖片描述

只用算紅色部分的就好了,因為下降的就是虧錢,沒必要買呀!

func maxProfit2(prices []int) int {
	length := len(prices)
	if length < 2 {
		return 0
	}
	i := 0
	for ; i < length - 1 && prices[i] > prices[i + 1]; i++{ //先找到低谷
	}
	res := 0
	for i < length - 1{
		j := i + 1
		for ; j < length; j++ { 
			if prices[j]
< prices[j - 1] { //找到上升曲線的轉折點 res += prices[j - 1] - prices[i] i = j //作為下一個買入點 break } } if j == length { //結束迴圈 if prices[j - 1] > prices[i] { //避免最後一條曲線是一個上升曲線,沒法找到轉折點 res += prices[j-1] - prices[i] } break } } return res }

動態規劃方法求解

這套題總過有6個,但是都是基於下面這個狀態轉移方程來進行的。這裡需要說明一下三維陣列 d p ∈ dp \in dp {n, k, 2} $對應的狀態以及意義, n為第幾天,k當前已買入次數, 2表示當前買賣狀態,0 為不持有股票, 1為持有股票。

狀態轉移方程為:


d p [ i ] [ k ] [ 0 ] = m a x ( d p [ i − 1 ] [ k ] [ 0 ] , d p [ i − 1 ] [ k ] [ 1 ] + p r i c e s [ i − 1 ] ) dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i-1]) dp[i][k][0]=max(dp[i1][k][0],dp[i1][k][1]+prices[i1])


情況一: 保持不買
情況二: 在前一天,第k次的買入的情況下以今天價格賣出去 ,這裡k是因為k是對應上的​



d p [ i ] [ k ] [ 1 ] = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , d p [ i − 1 ] [ k − 1 ] [ 0 ] − p r i c e s [ i − 1 ] ) dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i-1]) dp[i][k][1]=max(dp[i1][k][1],dp[i1][k1][0]prices[i1])


情況一: 保持不賣
情況二: 在前一天,第k次的沒有買入的的情況下買入今天的股票, 這裡k-1是因為要確保還能買入


邊界情況則是:

d p [ 0 ] [ j ] [ 1 ] = M I N I N T , j ∈ 1 , 2 , . . . , k dp[0][j][1] = MININT, j \in 1, 2, ..., k dp[0][j][1]=MININT,j1,2,...,k

d p [ 0 ] [ j ] [ 0 ] = 0 , j ∈ 1 , 2 , . . . , k dp[0][j][0] = 0, j \in 1, 2, ..., k dp[0][j][0]=0,j1,2,...,k


​初始條件下不可能買到股票,也就是持有股票的情況置為最小值,而狀態為不持有股票情況下,利潤就是為0

121. 買賣股票的最佳時機

func maxProfit(prices []int) int {
	length := len(prices)
    dp := make([][2]int, length + 1) //特殊只能買賣一次,就沒有必要設第二維度k
    dp[0][1] = math.MinInt32
    for i := 1; i <= length; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i-1])
        dp[i][1] = max(dp[i-1][1], -prices[i-1]) // 只能買賣一次,所以買賣情況只能假定前面啥都沒買賣,也就是錢都為0
    }
    return dp[length][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

122. 買賣股票的最佳時機 II

func maxProfit(prices []int) int {
	length := len(prices)
    dp := make([][2]int, length + 1) //沒有買賣次數限制,就沒有必要設第二維度k
    dp[0][1] = math.MinInt32
    for i := 1; i <= length; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i-1])
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i-1]) // 可以買賣多次,因此可以檢查前面的dp[i][0]
    }
    return dp[length][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

123. 買賣股票的最佳時機 III

func maxProfit(prices []int) int {
    length := len(prices)
    //三維陣列 {n, k, 2} n為第幾天,k當前已買入次數, 2表示當前買賣狀態,0 為不持有股票, 1 為持有股票
    dp := make([][][]int, length + 1)
    for i := 0; i <= length; i++ {
        dp[i] = make([][]int, 3)
        for j := 0; j < 3; j ++ {
            dp[i][j] = make([]int, 2)
        }
    }
    for k := 0; k < 3; k++ {
        dp[0][k][1] = math.MinInt32
    }
    for i := 1; i <= length; i++ {
        for k := 1; k < 3; k++ { 
            dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i-1]) 
            // 情況一:保持不買 情況二: 在前一天,第k次的買入的情況下以今天價格賣出去 ,這裡k是因為k是對應上的
            dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i-1])
            // 情況一:保持不賣 情況二: 在前一天,第k次的沒有買入的的情況下買入今天的股票, 這裡k-1是因為要確保還能買入
        }
    }
    return dp[length][2][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

188. 買賣股票的最佳時機 IV

func maxProfit(k int, prices []int) int {
    length := len(prices)
    //三維陣列 {n, k, 2} n為第幾天,k當前已買入次數, 2表示當前買賣狀態,0 為不持有股票, 1 為持有股票
    dp := make([][][]int, length + 1)
    for i := 0; i <= length; i++ {
        dp[i] = make([][]int, k+1)
        for j := 0; j < k+1; j ++ {
            dp[i][j] = make([]int, 2)
        }
    }
    for j := 0; j < k+1; j++ {
        dp[0][j][1] = math.MinInt32
    }
    for i := 1; i <= length; i++ {
        for j := 1; j < k+1; j++ { 
            dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i-1]) 
            // 情況一:保持不買 情況二: 在前一天,第k次的買入的情況下以今天價格賣出去 ,這裡k是因為k是對應上的
            dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i-1])
            // 情況一:保持不賣 情況二: 在前一天,第k次的沒有買入的的情況下買入今天的股票, 這裡k-1是因為要確保還能買入
        }
    }
    return dp[length][k][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

714. 買賣股票的最佳時機含手續費

func maxProfit(prices []int, fee int) int {
    length := len(prices)
    dp := make([][2]int, length + 1)
    dp[0][0] = 0
    dp[0][1] = math.MinInt32
    for i := 1; i <= length; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i-1] - fee)
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i-1]) // 可以買賣多次,因此可以檢查前面的dp[i][0]
    }
    return dp[length][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

309. 最佳買賣股票時機含冷凍期

func maxProfit(prices []int) int {
    length := len(prices)
    dp := make([][2]int, length + 1)
    dp[0][1] = math.MinInt32  //這種情況就是避免prices長度為0的情況
    for i := 1; i <= length; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i-1])
        if i - 2 >= 0 {
            dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i-1]) // 可以買賣多次,但是有冷凍期,所以檢查的是前一天的情況
        } else {
            dp[i][1] = max(dp[i-1][1],  -prices[i-1]) // 避免小於0的狀態,這裡就是不存在的情況下dp利潤值為0

        }
        
    }
    return dp[length][0]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}