123. 买卖股票的最佳时机 III
题目描述
给定一个数组,它的第 i 个元素是第 i 天的股票价格。设计一个算法来计算你所能获取的最大利润,最多可以完成 两笔交易。
注意事项:
- 你不能同时进行多笔交易(在买入新的股票之前必须先卖出之前的股票)。
- 一次交易包括买入和卖出股票。
示例
示例 1:
输入:
prices = [3,3,5,0,0,3,1,4]
输出:
6
解释:
- 在第 4 天(股票价格为 0)买入,在第 6 天(股票价格为 3)卖出,利润为 3 - 0 = 3。
- 随后,在第 7 天(股票价格为 1)买入,在第 8 天(股票价格为 4)卖出,利润为 4 - 1 = 3。
- 总利润为 3 + 3 = 6。
示例 2:
输入:
prices = [1,2,3,4,5]
输出:
4
解释:
- 在第 1 天买入,在第 5 天卖出,利润为 5 - 1 = 4。
示例 3:
输入:
prices = [7,6,4,3,1]
输出:
0
解释:
- 由于股票价格在整个时间段内不断下降,没有交易可以完成,最大利润为 0。
示例 4:
输入:
prices = [1]
输出:
0
解释:
- 只有一天的数据,无法进行任何交易,最大利润为 0。
解题思路
我们可以使用动态规划来解决这个问题,设定一个 dp
数组,记录在不同状态下的最大利润。
状态定义:
dp[i][1]
:第i
天时,持有股票并且完成了第一次买入。dp[i][2]
:第i
天时,不持有股票并且完成了第一次卖出。dp[i][3]
:第i
天时,持有股票并且完成了第二次买入。dp[i][4]
:第i
天时,不持有股票并且完成了第二次卖出。
由于我们有两笔交易,所以状态总共包括 4 个阶段。每一天,我们都可以选择继续持有股票、卖出股票或者买入股票。
初始化时,将所有状态初始化为极小值(-∞),以确保从头开始时,必须通过买入才能开始盈利。
动态规划转移方程:
dp[i][1] = max(dp[i-1][1], -prices[i-1])
:表示第一次买入股票,选择继续持有或者当天买入。dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i-1])
:表示第一次卖出股票,选择继续持有第一次卖出的状态,或者在第i
天卖出。dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i-1])
:表示第二次买入股票,选择继续持有第二次买入的状态,或者在第i
天买入。dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i-1])
:表示第二次卖出股票,选择继续持有第二次卖出的状态,或者在第i
天卖出。
最终结果:
- 最终答案为
max(dp[n][4], dp[n][2])
,即完成最多两次交易后的最大利润,可能是仅完成第一次卖出的利润或第二次卖出的利润。
代码实现
from typing import List
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n == 0:
return 0
# dp[i][1]: 持有股票并且第一次买入
# dp[i][2]: 不持有股票并且卖出了第一次股票
# dp[i][3]: 持有股票并且第二次买入
# dp[i][4]: 不持有股票并且卖出了第二次股票
dp = [[-2e9] * 5 for _ in range(n + 1)] # 初始化为极小值
for i in range(1, n + 1):
dp[i][1] = max(dp[i-1][1], -prices[i-1]) # 第一次买入股票
dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i-1]) # 第一次卖出股票
dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i-1]) # 第二次买入股票
dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i-1]) # 第二次卖出股票
# 返回完成最多两次交易后的最大利润
return max(0, dp[n][4], dp[n][2]) # 可以是第二次卖出(dp[n][4])或者仅卖出第一次股票(dp[n][2])
时间复杂度与空间复杂度
- 时间复杂度:
O(n)
,其中n
为价格数组的长度。我们只需要遍历一次价格数组,更新dp
数组的值。 - 空间复杂度:
O(n)
,我们使用了一个n+1
长度的二维数组dp
来记录状态。
结语
该问题通过动态规划来模拟最大利润的计算过程,考虑了两次交易的情况。通过合理的状态转换,能够高效地得出结果。