經典dp--股票買賣系列系列之GO寫法
經典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[i−1][k][0],dp[i−1][k][1]+prices[i−1])
情況一: 保持不買
情況二: 在前一天,第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[i−1][k][1],dp[i−1][k−1][0]−prices[i−1])
情況一: 保持不賣
情況二: 在前一天,第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,j∈1,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,j∈1,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
}