按策略买卖股票的最佳时机-LeetCode
3652. 按策略购买股票的最佳时机
🎯 问题描述
给你两个整数数组 prices 和 strategy,其中:
prices[i]表示第i天某股票的价格。strategy[i]表示第i天的交易策略,其中:-1表示买入一单位股票。0表示持有股票。1表示卖出一单位股票。
同时给你一个 偶数 整数 k,你可以对 strategy 进行 最多一次 修改。一次修改包括:
- 选择
strategy中恰好k个 连续 元素。 - 将前
k / 2个元素设为0(持有)。 - 将后
k / 2个元素设为1(卖出)。
利润 定义为所有天数中strategy[i] * prices[i]的 总和 。
返回你可以获得的 最大 可能利润。
说明:
-
这是一个滑动窗口结合前缀和的问题。
-
核心策略:
- 首先计算不修改任何操作时的基础总利润
base_profit。 - 利用滑动窗口枚举所有可能的长度为
k的修改区间。 - 对于每个窗口,将其分为前半段(长度
k//2)和后半段。 - 计算“反转后半段操作”后的新收益,并与原收益对比,取最大值。
- 利用前缀和数组快速计算区间内的策略利润与价格总和。
- 首先计算不修改任何操作时的基础总利润
-
示例:
-
示例 1:
1 | 假设 prices = [1, 2, 3, 4], strategy = [1, 1, 1, 1], k = 2 |
💻 解题思路
思路1:双前缀和 + 滑动窗口
核心逻辑
- 构建前缀和数组:
pre[i+1]:表示前i个位置按照strategy操作的累积利润。公式:。pre_price[i+1]:表示前i个位置的价格总和。公式:。
- 计算基础利润:
base_profit = pre[n],即完全不修改时的总利润。
- 滑动窗口枚举与优化:
- 遍历所有可能的窗口起点
i(窗口范围[i, i+k-1])。 - 分割窗口:将窗口分为前半段(长度
l = k//2)和后半段。 - 计算反转后的利润
p3:- 原窗口利润
p1。 - 反转后半段意味着:原本加上
price,现在减去price(或者反之)。 - 变化量 = (新收益) - (旧收益) = 。
- 代码中的
p3计算逻辑实际上是在模拟这种反转带来的总收益变化。 - 公式解析:
p3 = p1 - (原后半段利润) + (后半段价格总和 - 前半段调整)。这里的逻辑稍微复杂,实际上是在计算:前半段保持不变 + 后半段反转后的收益。
- 原窗口利润
- 更新全局最大值:
- 总利润 =
base_profit - p1 + max(p1, p3)。 - 即:总利润 = 基础利润 + (优化带来的增量)。
- 总利润 =
- 遍历所有可能的窗口起点
代码实现
1 | class Solution: |
📊 性能分析
- 时间复杂度:
- 构建前缀和数组:。
- 滑动窗口遍历:,每次窗口内的计算(包括
p3的推导)都是 。 - 总体线性时间,能够处理大规模数据。
- 空间复杂度:
- 使用了两个长度为 的辅助数组
pre和pre_price。
- 使用了两个长度为 的辅助数组
思考
-
前缀和的双重作用:
pre数组用于快速计算策略收益。pre_price数组用于快速获取区间内的价格总和,这在涉及“反转操作”(如买入变卖出,收益变化为 )的计算中非常有用。
-
p3的公式推导:- 代码中的
p3计算较为晦涩:p1 - (原后半段利润) + p_price - p_half。 - 这通常对应于题目特定的变换规则。例如,如果题目允许将后半段的“做空”变为“做多”,收益变化不仅仅是取反,还可能涉及价格本身的加减。
- 理解这一点的关键在于:
p_price代表纯价格加和,而pre代表带策略符号的加和。
- 代码中的
-
贪心策略:
- 算法本质上是在寻找一个“最优区间”进行修改。
- 对于每个区间,我们只关心修改后是否比不修改更好(
max(p1, p3)),这是一种局部贪心,配合全局遍历得到全局最优。
-
边界处理:
l = k // 2将窗口一分为二。当k为奇数时,后半段比前半段多一个元素。- 当
l=0时(即k=1),前半段为空,代码通过if l > 0进行了兼容,保证了逻辑的正确性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 笺札!






