股票市场交易策略优化解析
股票交易策略优化问题是一个典型的动态规划问题,在这个问题中,我们需要设计一种算法,帮助我们在遵守一系列规则的情况下,最大化股票交易收益。题目给出了以下交易规则:
- 每次交易必须先买入股票,后卖出股票,且在卖出后存在一天的冷冻期。
- 卖出后的一天,不能再次买入股票,直到冷冻期结束。
本文将从解题思路、代码实现到优化的过程,逐步带你深入理解如何通过动态规划解决这个问题。
问题背景和目标
问题描述
给定一个整数数组 stocks,其中 stocks[i] 表示第 i 天的股票价格。我们需要计算在遵守以下交易规则的情况下,能够获得的最大利润:
- 可以多次进行买卖交易,但每次卖出后有一天的冷冻期,冷冻期内不能再次购买股票。
输入输出示例
示例1
输入:stocks = [1, 2]
输出:1
解释:在第1天买入,第2天卖出,最大收益为 2 - 1 = 1。
示例2
输入:stocks = [1, 2, 3, 0, 2]
输出:3
解释:在第1天买入,第3天卖出,收益 3 - 1 = 2;第4天买入,第5天卖出,收益 2 - 0 = 2。总收益为 2 + 1 = 3。
解题思路
本题的核心是动态规划(DP),我们通过设计状态转移方程来逐步求解问题。首先,我们需要定义三个关键状态:
- 持有股票状态
hold:表示第i天持有股票的最大利润。 - 不持有股票且非冷冻期
free:表示第i天不持有股票且可以进行买入操作的最大利润。 - 不持有股票且处于冷冻期
cold:表示第i天不持有股票,但由于冷冻期,不能进行买入操作的最大利润。
状态转移方程
-
持有状态
hold[i]第i天持有股票的情况:- 第
i-1天已经持有股票,今天继续持有。 - 第
i-1天未持有股票并非冷冻期,今天买入。 因此,状态转移方程为:
hold[i]=max(hold[i−1],free[i−1]−stocks[i])
- 第
-
未持有状态(非冷冻期)
free[i]第i天未持有股票且非冷冻期的情况:- 第
i-1天未持有股票且非冷冻期,今天继续不买入。 - 第
i-1天未持有股票且处于冷冻期,可以开始买入。 因此:
free[i]=max(free[i−1],cold[i−1])
- 第
-
未持有状态(冷冻期)
cold[i]第i天不持有股票且冷冻期的情况:- 第
i-1天卖出了股票,进入冷冻期。 因此:
cold[i]=hold[i−1]+stocks[i]
- 第
边界条件
- 第0天:
- 持有股票:
hold[0] = -stocks[0](买入股票)。 - 未持有股票且非冷冻期:
free[0] = 0(没有操作)。 - 未持有股票且冷冻期:
cold[0] = 0(不存在冷冻期)。
- 持有股票:
代码实现
初始版本(使用数组存储状态)
首先实现一个版本,使用数组来存储每个状态的最大利润。
public class Main {
public static int solution(int[] stocks) {
int n = stocks.length;
if (n < 2) return 0;
int[] hold = new int[n];
int[] free = new int[n];
int[] cold = new int[n];
// 初始化状态
hold[0] = -stocks[0]; // 第0天买入
free[0] = 0; // 第0天没有操作
cold[0] = 0; // 第0天没有冷冻期
// 动态规划求解
for (int i = 1; i < n; i++) {
hold[i] = Math.max(hold[i - 1], free[i - 1] - stocks[i]);
free[i] = Math.max(free[i - 1], cold[i - 1]);
cold[i] = hold[i - 1] + stocks[i];
}
// 返回最大值:最终不持有股票的状态
return Math.max(free[n - 1], cold[n - 1]);
}
public static void main(String[] args) {
System.out.println(solution(new int[]{1, 2}) == 1);
System.out.println(solution(new int[]{1, 2, 3, 0, 2}) == 3);
System.out.println(solution(new int[]{2, 3, 4, 5, 6, 7}) == 5);
System.out.println(solution(new int[]{1, 6, 2, 7, 13, 2, 8}) == 12);
}
}
优化版本(使用常量空间)
在上述实现中,我们用到了三个数组来存储 hold、free 和 cold 的状态。每一天的状态只与前一天的状态相关,因此可以将这些数组压缩成常量空间的变量,只保留前一天的状态来进行计算。
public class Main {
public static int solution(int[] stocks) {
int n = stocks.length;
if (n < 2) return 0;
// 使用常量空间
int hold = -stocks[0]; // 第0天买入
int free = 0; // 第0天没有操作
int cold = 0; // 第0天没有冷冻期
// 动态规划求解
for (int i = 1; i < n; i++) {
int prevHold = hold;
int prevFree = free;
hold = Math.max(prevHold, prevFree - stocks[i]);
free = Math.max(prevFree, cold);
cold = prevHold + stocks[i];
}
// 返回最大值:最终不持有股票的状态
return Math.max(free, cold);
}
public static void main(String[] args) {
System.out.println(solution(new int[]{1, 2}) == 1);
System.out.println(solution(new int[]{1, 2, 3, 0, 2}) == 3);
System.out.println(solution(new int[]{2, 3, 4, 5, 6, 7}) == 5);
System.out.println(solution(new int[]{1, 6, 2, 7, 13, 2, 8}) == 12);
}
}
优化过程分析
初始版本的空间复杂度
在初始版本中,我们使用了 O(n) 的空间来存储每一天的状态。这意味着,如果输入数组的长度很大,空间开销会非常大。对于这类问题,我们可以尝试优化空间复杂度,通常可以通过状态压缩来减少空间开销。
优化后的版本
优化后的版本将空间复杂度压缩到了 O(1),因为我们只需要保留前一天的状态信息。这种优化不仅节省了空间,还提高了代码的效率和简洁性。
时间复杂度
两种实现的时间复杂度都是 O(n),因为我们只需要遍历股票价格数组一次来计算最大利润。因此,时间复杂度在两者之间没有变化。
总结
通过这道题,我们了解了如何使用动态规划来解决具有冷冻期限制的股票交易问题。通过分析并逐步优化空间复杂度,我们将初始版本的空间复杂度从 O(n) 优化到 O(1),使得算法更加高效。
动态规划是解决这类问题的关键技巧,它不仅能帮助我们高效地解决问题,还能为我们处理更多复杂问题提供思路。希望本文的分析和优化过程对你理解动态规划有所帮助,并能激发你进一步思考和优化解决方案的能力。