leetcode买卖股票共有6道题,比较经典,虽然有多种解决方案,但是最重要的通用的一种是动态规划。
Tags array | dynamic-programming | greedy
好了,看下题目综述,其中的6个条件,是不同题包含的。
题目综述:
买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
条件:只许一次交易 / 允许多次交易 / 只许2次交易 / 允许k次交易 / 含冷冻期 / 含手续费
设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
本质来说,会允许k次交易就好了。值得注意的是含冷冻期的实现。其他的都是子集。
这一个问题,labuladong说的很好,可以参考。应用转态和选择的思路。
这个题,我为数不多的仔细写写吧。看两种情况,一种是可以进行一次操作的,一种是能进行很多次操作的。
用实际例子比较好理解,如下:
比如给出的例子是这样的:
p
r
i
c
e
s
=
[
3
,
2
,
1
,
7
,
9
,
4
,
6
]
prices = [3, 2, 1, 7, 9, 4, 6]
prices=[3,2,1,7,9,4,6]
- 如过只能进行一次操作。持有为1,未持有为0。过程dp数组如下:
转态转移方程:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], -prices[i]) dp[i][1]=max(dp[i−1][1],−prices[i])
0(未持有) | 0(初始化) | 0 | 0 | 0 | 6 | 8 | 8 | 8 |
---|---|---|---|---|---|---|---|---|
1 (持有) | -inf(初始化) | -3 | -2 | -1 | -1 | -1 | -1 | -1 |
持有的后面都是-1,代表-1是最低价买入了。
未持有后面都是8,代表后面的都卖不到8了。
- 如过只能进行很多次操作。持有为1,未持有为0。过程dp数组如下:
转态转移方程:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])
未持有 | 0(初始化) | 0 | 0 | 0 | 6 | 8 | 8 | 10 |
---|---|---|---|---|---|---|---|---|
持有 | -inf(初始化) | -3 | -2 | -1 | -1 | -1 | 4 | 4 |
思路就是低价买入,高价卖出。赚价差。
再比如
p
r
i
c
e
s
=
[
1
,
9
,
6
,
10
]
prices = [1, 9, 6, 10]
prices=[1,9,6,10]
如果只操作一次,那就是1买10卖。如果操作多次,现在9卖,然后买入6再到10卖。
- 一次操作
未持有 | 0(初始化) | 0 | 8 | 8 | 9 |
---|---|---|---|---|---|
持有 | -inf(初始化) | -1 | -1 | -1 | -1 |
- 多次操作
未持有 | 0(初始化) | 0 | 8 | 8 | 12 |
---|---|---|---|---|---|
持有 | -inf(初始化) | -1 | -1 | 2 | 2 |
这个题就是举例子,然后就慢慢懂了,感受到了。
好了,写统一的代码。
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
n = len(prices)
if k>(n//2):
return self.infk(prices)
# i, k, 0/1; 分别是存i天,k次操作,未持有/持有
dp = [[[0,0] for _ in range(k+1)] for _ in range(n+1)]
for j in range(k+1):
dp[0][j][1] = -float('inf')
for i in range(1,n+1):
for j in range(1,k+1):
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1]+prices[i-1])
dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i-1])
return dp[n][k][0]
def infk(self, prices):
n = len(prices)
dp = [[0,0] for _ in range(n+1)]
dp[0][1] = -float('inf')
for i in range(1,n+1):
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])
return dp[n][0]
注意初始化, d p [ 0 ] [ k ] [ 1 ] dp[0][k][1] dp[0][k][1]是第1天就持有,所以为 − f l o a t ( ′ i n f ′ ) -float('inf') −float(′inf′),不可能,负无穷。
操作k次,转态转移方程:
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
]
)
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])
dp[i][k][0]=max(dp[i−1][k][0],dp[i−1][k][1]+prices[i]) # 未持有 = max(上次就未持有,上次持有+卖)
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
]
)
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])
dp[i][k][1]=max(dp[i−1][k][1],dp[i−1][k−1][0]−prices[i]) # 持有 = max(上次就持有,上次为持有+买)
操作1次,转态转移方程,省掉k,因为初始化为0:
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
)
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
1
]
,
−
p
r
i
c
e
s
[
i
]
)
dp[i][1] = max(dp[i-1][1], -prices[i])
dp[i][1]=max(dp[i−1][1],−prices[i])
操作无穷次,转态转移方程,省掉k,注意与操作1次的区别,因为k无穷大,所以k-1==k:
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
)
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
1
]
,
d
p
[
i
−
1
]
[
0
]
−
p
r
i
c
e
s
[
i
]
)
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])
含冷冻期,无限次购买,变成i-2,两天前未持有,然后买入:
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
)
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
1
]
,
d
p
[
i
−
2
]
[
0
]
−
p
r
i
c
e
s
[
i
]
)
dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i])
dp[i][1]=max(dp[i−1][1],dp[i−2][0]−prices[i])
含手续费:
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
−
f
e
e
)
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]-fee)
dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i]−fee)
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
1
]
,
d
p
[
i
−
1
]
[
0
]
−
p
r
i
c
e
s
[
i
]
)
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])
好了,就这样了,最好吧答案/状态转移矩阵 记住。。。我捋一会也快晕了。。。不行就看看举的例子,自己也举例子一下。
好了。