188. 买卖股票的最佳时机 IV
188. 买卖股票的最佳时机 IV
题目来源
题目分析
给定一个整数数组
prices
,其中prices[i]
表示第i
天的股票价格,设计一个算法来计算你所能获取的最大利润。你最多可以完成k
笔交易。也就是说,你最多可以买k
次,卖k
次。
题目难度
- 难度:困难
题目标签
- 标签:动态规划, 数组
题目限制
1 <= k <= 100
1 <= prices.length <= 1000
0 <= prices[i] <= 1000
解题思路
思路1:动态规划
-
问题定义:
- 定义
dp[i][j][0]
表示第i
天完成j
笔交易且不持有股票的最大利润。 - 定义
dp[i][j][1]
表示第i
天完成j
笔交易且持有股票的最大利润。
- 定义
-
状态转移:
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i-1])
:第i
天不持有股票的最大利润,可能是前一天也不持有,或者当天卖出股票后的最大利润。dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i-1])
:第i
天持有股票的最大利润,可能是前一天就持有,或者在完成j-1
笔交易后当天买入股票后的最大利润。- 注意这里
j - 1
表示我们用掉了第j
次交易的机会来买入股票。然后由于买入和卖出都是k
次那么我们直接计数买入或者卖出的次数都行,也就是可以改成第二个式子dp[i-1][j][0] - prices[i-1]
,第一个式子dp[i-1][j-1][1] + prices[i-1]
.
-
初始化:
- 对于所有的
k
,dp[0][k][0] = 0
,即第0天不持有股票的最大利润为0。 - 对于所有的
k
,dp[0][k][1] = -prices[0]
,即第0天持有股票的最大利润为-prices[0]
。
- 对于所有的
-
最终结果:
- 返回
dp[n][k][0]
即为第n
天完成k
笔交易且不持有股票时的最大利润。
- 返回
核心算法步骤
- 动态规划:
- 初始化
dp
三维数组,表示不同天数、交易次数和是否持有股票的状态。 - 通过嵌套循环遍历天数和交易次数,逐步更新
dp
数组中的值。 - 最终返回
dp[n][k][0]
。
- 初始化
代码实现
以下是求解买卖股票的最佳时机 IV 的 Java 代码:
/**
* 188. 买卖股票的最佳时机 IV
* @param k 交易次数
* @param prices 股票价格数组
* @return 最大利润
*/
public int maxProfit(int k, int[] prices) {
int n = prices.length;
if (n == 0) return 0;
// 当 k 大于 n/2 时,问题等同于无限次交易
if (k > n / 2) return maxProfitUnlimited(prices);
int[][][] dp = new int[n + 1][k + 1][2];
for (int i = 0; i <= k; i++) {
dp[0][i][0] = 0;
dp[0][i][1] = -prices[0];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]);
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]);
}
}
return dp[n][k][0];
}
// 辅助函数:当 k 大于 n/2 时,等同于无限次交易
private int maxProfitUnlimited(int[] prices) {
int n = prices.length;
int maxProfit = 0;
for (int i = 1; i < n; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
代码解读
dp[i][j][0]
表示第i
天完成j
笔交易且不持有股票的最大利润。dp[i][j][1]
表示第i
天完成j
笔交易且持有股票的最大利润。- 当
k
大于n/2
时,问题等同于可以进行无限次交易的情况,因此使用辅助函数maxProfitUnlimited
处理。
性能分析
- 时间复杂度:
O(n*k)
,其中n
是prices
数组的长度,k
是最多允许的交易次数。 - 空间复杂度:
O(n*k*2)
,需要n*k*2
大小的dp
数组来存储动态规划的状态值。
测试用例
你可以使用以下测试用例来验证代码的正确性:
int k1 = 2;
int[] prices1 = {2, 4, 1};
int result1 = maxProfit(k1, prices1);
System.out.println(result1); // 输出: 2
int k2 = 2;
int[] prices2 = {3, 2, 6, 5, 0, 3};
int result2 = maxProfit(k2, prices2);
System.out.println(result2); // 输出: 7
int k3 = 3;
int[] prices3 = {1, 2, 4, 2, 5, 7, 2, 4, 9, 0};
int result3 = maxProfit(k3, prices3);
System.out.println(result3); // 输出: 15
扩展讨论
优化写法
- 空间优化:可以将空间复杂度优化为
O(k*2)
,通过仅保存前一天的状态来更新当前状态。
其他实现
- 对于大
k
的情况,可以考虑优化为无限次交易的情况。
总结
买卖股票的最佳时机 IV 问题,通过动态规划方法解决,考虑了最多可以进行 k
笔交易的限制,使得问题更具挑战性。通过合理的状态转移方程可以有效求解该问题。