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