問題121:給定一個數(shù)組,它的第i
個元素是給定股票第i
天的價格。如果你最多只能買入和賣出兩次,設(shè)計一個算法來計算你所能獲取的最大利潤。注意:你不能在買入股票前賣出股票。
解法一:切成兩段,每段分別用第121題的方法,求兩段和最大值;
class Solution:
def maxProfit(self, prices: List[int]) -> int:
def maxPro(prices):
if not prices:
return 0
dif = []
predp = 0
ans = 0
for i in range(1, len(prices)):
dif = prices[i] - prices[i-1]
predp = max(dif, dif + predp)
ans = max(predp, ans)
return ans
max_pro = 0
for mid in range(0, len(prices)):
cur_pro = maxPro(prices[:mid+1]) + maxPro(prices[mid+1:])
if cur_pro > max_pro:
max_pro = cur_pr
return max_pro
這種方法比較直觀,但是會超時。
解法二:動態(tài)規(guī)劃。
這種解法,比較難理解,也可能是我太菜了,自己從頭到尾推了一遍,才勉強(qiáng)理解。
舉個例子,對于[7, 1, 5, 3, 6, 4]
, 其狀態(tài)轉(zhuǎn)移圖如下所示。先遍歷列,再遍歷行。注意,輸出的是max(0, max(dp[len(prices)][2], dp[len(prices)][4]))
,因?yàn)樽畲罄麧櫦瓤赡茉诘谝淮钨u出時獲得,又可能在第二次賣出時獲得。如果操作一定虧損,干脆不進(jìn)行任何操作,直接輸出0
。
完整代碼:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [[float('-inf') for _ in range(5)] for _ in range(len(prices)+1)]
for i in range(len(prices)+1):
dp[i][0] = 0
for i in range(1, len(prices)+1):
for j in range(1, 5):
if j % 2 == 1:
dp[i][j] = max(dp[i-1][j-1] - prices[i-1], dp[i-1][j])
else:
dp[i][j] = max(dp[i-1][j-1] + prices[i-1], dp[i-1][j])
return max(0, max(dp[len(prices)][2], dp[len(prices)][4]))
運(yùn)行結(jié)果:
與背包問題類似,我們可以對該程序進(jìn)行優(yōu)化,優(yōu)化到一維。
注意,要從后往前遍歷,因?yàn)橛嬎?img class="math-inline" src="https://math.jianshu.com/math?formula=dp%5Bj%5D" alt="dp[j]" mathimg="1">的時候,
的值依賴于
,而此時的
應(yīng)該是第
輪的,而不是第
輪的。所以,一定要在更新
之前更新
,因此
要從后往前遍歷。同時,這也是先遍歷
,再遍歷
的原因。
優(yōu)化代碼:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [float('-inf') for _ in range(5)]
dp[0] = 0
for i in range(1, len(prices)+1):
#j從后往前遍歷
for j in range(4, 0, -1):
if j % 2 == 1:
dp[j] = max(dp[j-1] - prices[i-1], dp[j])
else:
dp[j] = max(dp[j-1] + prices[i-1], dp[j])
return max(0, max(dp[2], dp[4]))
運(yùn)行結(jié)果: