1. 题目连接:188. 买卖股票的最佳时机 IV
2. 题目描述:
给你一个整数数组
prices
和一个整数k
,其中prices[i]
是某支给定的股票在第i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成
k
笔交易。也就是说,你最多可以买k
次,卖k
次。**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3] 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。 随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
提示:
1 <= k <= 100
1 <= prices.length <= 1000
0 <= prices[i] <= 1000
3. 解法(动态规划):
3.1 算法思路:
1. 状态表示:
f[i][j]
表示:第i
天结束后,完成了j
笔交易,此时处于有股票状态的最大收益
g[i][j]
表示:第i
天结束后,完成了j
笔交易,此时处于无股票状态的最大收益
2. 状态转移方程:
对于 f[i][j]
,我们也有两种情况能在第i
天结束后,完成j
笔交易,此时手中有股票状态:
-
在
i-1
天的时候,手里有股票,并且交易了j
次,在第i
天的时候,啥也不干,此时的收益为f[i-1][j]
-
在
i-1
天的时候,手里没有股票,并且交易了j
次,在第i
天的时候,买了股票。那么i
天结束之后,我们就有股票了。此时的收益为g[i-1][j]-prices[i]
-
上面两种情况我们需要的是最大值,因此
f
的状态转移方程为:f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i])
对于 g[i][j]
,我们有下面两种情况能在第i
天结束后,完成j
笔交易,此时手中没有股票的状态:
- 在
i-1
天的时候,手里没有股票,并且交易了j
次。在第i
天的时候,啥也不干,此时的收益为g[i-1][j]
- 在
i-1
天的时候,手里有股票,并且交易了j-1
次。在第i
天的时候,把股票卖了。那么i
天结束后,我们就交易了j
次,此时的收益为f[i-1][j-1]+prices[i]
- 上面两种情况,我需要的是最大值,因此
g
的状态转移方程为:g[i][j]=max(g[i-1][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++算法代码:
//优化点;我们的交易次数是不会超过整个天数的一半的,因此我们可以先把k处理一下,优化一下问题的规模 k=min(k,n/2)
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
const int INF = 0x3f3f3f3f; // 定义一个很大的数作为初始值
int n = prices.size(); // 获取价格数组的长度
k = min(k, n / 2); // 取k和n/2的较小值,避免重复交易
vector<vector<int>> f(n, vector<int>(k + 1, -INF)); // 初始化动态规划数组f,大小为n*(k+1),初始值为-INF
auto g = f; // 创建一个g指针指向f,用于后续操作
f[0][0] = -prices[0], g[0][0] = 0; // 初始化f和g的第一行
for (int i = 1; i < n; i++) {
for (int j = 0; j <= k; 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;
for (int j = 0; j <= k; j++) {
ret = max(ret, g[n - 1][j]); // 遍历g的最后一行,找到最大利润
}
return ret; // 返回最大利润
}
};