0188. 买卖股票的最佳时机 IV
0188. 买卖股票的最佳时机 IV
- 标签:数组、动态规划
- 难度:困难
题目链接
题目大意
给定一个数组 prices 代表一只股票,其中 prices[i] 代表这只股票第 i 天的价格。再给定一个整数 k,表示最多可完成 k 笔交易,且不能同时参与多笔交易(必须在再次购买前出售掉之前的股票)。
现在要求:计算所能获取的最大利润。
解题思路
动态规划求解。这道题是「0123. 买卖股票的最佳时机 III」的升级版,不过思路一样
最多可完成两笔交易意味着总共有三种情况:买卖一次,买卖两次,不买卖。
具体到每一天结束总共有 2 * k + 1 种状态:
- 未进行买卖状态;
- 第
1次买入状态; - 第
1次卖出状态; - 第
2次买入状态; - 第
2次卖出状态。 - ...
- 第
m次买入状态。 - 第
m次卖出状态。
因为买入、卖出为两种状态,干脆我们直接让偶数序号表示买入状态,奇数序号表示卖出状态。
所以我们可以定义状态 dp[i][j] ,表示为:第 i 天第 j 种情况(0 <= j <= 2 * k)下,所获取的最大利润。
注意:这里第 j 种情况,并不一定是这一天一定要买入或卖出,而是这一天所处于的买入卖出状态。比如说前一天是第一次买入,第二天没有操作,则第二天就沿用前一天的第一次买入状态。
接下来确定状态转移公式:
- 第
0种状态下显然利润为0,可以直接赋值为昨天获取的最大利润,即dp[i][0] = dp[i - 1][0]。 - 第
1次买入状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
dp[i][1] = dp[i - 1][1]。 - 第
1次买入:dp[i][1] = dp[i - 1][0] - prices[i]。
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
- 第
1次卖出状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][2] = dp[i - 1][2]。 - 第
1次卖出:dp[i][2] = dp[i - 1][1] + prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
- 第
2次买入状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
dp[i][3] = dp[i - 1][3]。 - 第
2次买入:dp[i][3] = dp[i - 1][2] - prices[i]。
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
- 第
2次卖出状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][4] = dp[i - 1][4]。 - 第
2次卖出:dp[i][4] = dp[i - 1][3] + prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
- ...
- 第
m次(j = 2 * m)买入状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][j] = dp[i - 1][j]。 - 第
m次买入:dp[i][j] = dp[i - 1][j - 1] - prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
- 第
m次(j = 2 * m + 1)卖出状态下可以有两种状态推出,取最大的那一种赋值:- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][j] = dp[i - 1][j]。 - 第
m次卖出:dp[i][j] = dp[i - 1][j - 1] + prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
下面确定初始化的边界值:
可以很明显看出第一天不做任何操作就是 dp[0][0] = 0,第 m 次买入(j = 2 * m)就是 dp[0][j] = -prices[i]。
第 m 次(j = 2 * m + 1)卖出的话,可以视作为没有盈利(当天买卖,价格没有变化),即 dp[0][j] = 0。
在递推结束后,最大利润肯定是无操作、第 m 次卖出这几种种情况里边,且为最大值。我们在维护的时候维护的是最大值,则第 m 次卖出所获得的利润肯定大于等于 0。而且,如果最优情况为 m - 1 笔交易,那么在转移状态时,我们允许在一天内进行多次交易,则 m - 1 笔交易的状态可以转移至 m 笔交易,最终都可以转移至 k 比交易。
所以最终答案为 dp[size - 1][2 * k]。size 为股票天数。
代码
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
size = len(prices)
if size == 0:
return 0
dp = [[0 for _ in range(2 * k + 1)] for _ in range(size)]
for j in range(1, 2 * k, 2):
dp[0][j] = -prices[0]
for i in range(1, size):
for j in range(1, 2 * k + 1):
if j % 2 == 1:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])
else:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i])
return dp[size - 1][2 * k]