1. 题目链接:123. 买卖股票的最佳时机 III
2. 题目描述:
给定一个数组,它的第
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 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1] 输出:0
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 105
3. 解法(动态规划):
3.1 算法思路:
1. 状态表示:
由于有买入和可交易两个状态,因此我们可以选择用两个数组,但是这道题里面还有交换次数的限制,因此我们还需要加上一堆,用来表示交易次数。其中:
f[i][j]
表示:第i
天结束后,完成了j
次交易,处于买入状态,此时的最大利润
g[i][j]
表示:第i
天结束后,完成了 j
次交易,处于卖出状态,此时的最大利润
2. 状态转移方程:
对于 f[i][j]
,我们有两种情况到这个状态:
-
在
i-1
天的时候,交易了j
次,处于买入状态,第i
天啥也不干即可。此时最大利润为:f[i-1][j]
-
在
i-1
天的时候,交易了j
次,处于卖出状态,第i
天的时候把股票买了。此时的最大利润为:g[i-1][j]-prices[i]
-
综上,我们要的是最大利润,因此是两者的最大值:
f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i])
对于 g[i][j]
,我们也有两种情况可以到达这个状态:
- 在
i-1
天的时候,交易了j
次,处于卖出状态,第i天啥也不干即可。此时的最大利润为:g[i-1][j]
- 在
i-1
天的时候,交易了j-1
次,处于买入状态,第i
天把股票卖了,然后就完成了j
次交易。此时的最大利润为:f[i-1][j-1]+prices[i]
,但是这个状态不一定存在,要先判断一下 - 综上,我们要的最大利润,因此状态转移方程为:
g[i][j]=g[i-1][j];
if(j>=1) g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
3. 初始化:
由于需要用到 i=0
时的状态,因此我们初始化第一行即可
-
当处于第
0
天的时候,只能处于买入过一次的状态,此时的收益为-prices[0]
,因此f[0][0]=-prices[0]
-
为了取
max
的时候,一些不存在的状态起不到干扰的作用,我们统统将它们初始化为-INF
(用INT_MIN
在计算过程中会有溢出的风险,这样INF
折半取0x3f3f3f3f
,足够小即可)
4. 填表顺序:
从上往下填每一行,每一行从左往右,两个表一起填
5. 返回值:
返回处于卖出状态的最大值,但是我们也不知道交易了几次,因此返回g
表最后一行的最大值
3.2 C++算法代码:
class Solution {
public:
const int INF = 0X3f3f3f3f; // 定义一个无穷大的常量
int maxProfit(vector<int>& prices) {
int n = prices.size(); // 获取价格数组的长度
vector<vector<int>> f(n, vector<int>(3, -INF)); // 初始化动态规划数组f,大小为n*3,初始值为-INF
auto g = f; // 创建一个g指针,指向f数组
f[0][0] = -prices[0], g[0][0] = 0; // 初始化f和g数组的第一个元素
// 遍历价格数组,更新动态规划数组f和g的值
for (int i = 1; i < n; i++) {
for (int j = 0; j < 3; j++) {
f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]); // 状态转移方程1
g[i][j] = g[i - 1][j]; // 状态转移方程2
if (j >= 1) {
g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]); // 状态转移方程3
}
}
}
int ret = 0; // 初始化最大利润为0
for (int j = 0; j < 3; j++) {
ret = max(ret, g[n - 1][j]); // 遍历g数组的最后一行,找到最大值
}
return ret; // 返回最大利润
}
};