描述
给定n天的股票值和允许的最大交易次数k,问最大利润。
已经买了一只股必须要卖出去才能买下一支股。
思路
dp。
当k>=n//2时,达到n天交易次数的饱和,这时,只要一有利润就会有收入。具体在实现上,相邻两天如果是股票值上升的,就记入利润。
其余情况,用dp。首先考虑状态,本题有两个,第几天和第几次交易。
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]代表第
i
i
i天第
j
j
j次交易完成后的利润。
状态转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
m
]
[
j
−
1
]
+
p
[
i
]
−
p
[
m
]
)
,
m
=
0
,
.
.
.
.
,
i
−
1
dp[i][j]=max(dp[i-1][j], dp[m][j-1]+p[i]-p[m]),m=0,....,i-1
dp[i][j]=max(dp[i−1][j],dp[m][j−1]+p[i]−p[m]),m=0,....,i−1
m
m
m代表第
j
j
j次交易买入的时间是第
m
m
m天。
于是有了下面的代码:
from typing import (
List,
)
class Solution:
"""
@param K: An integer
@param prices: An integer array
@return: Maximum profit
"""
def maxProfit(self, K, prices):
k = K
n = len(prices)
if k==0 or n==0:
return 0
if k >= n//2:
res = 0
for i in range(1, n):
if prices[i]-prices[i-1]>0:
res += prices[i]-prices[i-1]
return res
else:
dp = [[0]*(k+3) for i in range(n+3)]
for i in range(1, n):
for j in range(1, k+1):
diff = max([dp[k][j-1]-prices[k] for k in range(i)])
dp[i][j] = max(diff+prices[i], dp[i-1][j])
return dp[n-1][k]
这个代码运行超时,因为时间复杂度O(nnk),主要是求diff用了O(n)。可以优化一下。
diff实际上是
d
p
[
m
]
[
j
−
1
]
−
p
[
m
]
,
m
=
0
,
.
.
.
i
−
1
dp[m][j-1]-p[m],m=0,...i-1
dp[m][j−1]−p[m],m=0,...i−1,j-1是不动的,m是动的,可以把j-1代表的循环放外层,m
代表的循环放内存,每次循环的时候记录一下,就不需要额外的O(n)。具体是,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]的i和j的含义换一下,i代表第几次交易,j代表第几天。于是有:
from typing import (
List,
)
class Solution:
"""
@param K: An integer
@param prices: An integer array
@return: Maximum profit
"""
def maxProfit(self, K, prices):
k = K
n = len(prices)
if k==0 or n==0:
return 0
if k >= n//2:
res = 0
for i in range(1, n):
if prices[i]-prices[i-1]>0:
res += prices[i]-prices[i-1]
return res
else:
dp = [[0]*(n+3) for i in range(k+3)]
for i in range(1, k+1):
diff = float('-inf')
for j in range(1, n):
diff = max(diff, dp[i-1][j-1]-prices[j-1])
dp[i][j] = max(diff+prices[j], dp[i][j-1])
return dp[k][n-1]
通过。注意diff初始化,不能初始化为0,要初始化为float(’-inf’)代表负无穷。
目标是要求最大的diff,所以初始化为最小值。
因为diff代表第i-1次交易前j-1天的最大利润,利润可以为负的,代表亏损,
如果初始化为0,就可能比实际利润要大,显然不合理。
当初始化为最小或最大的值的时候要考虑当下的题目的实际情况。