LeetCode 3652: 按策略买卖股票的最佳时机

题目理解

给定价格数组 prices 和策略数组 strategy,策略可以是:

  • -1: 买入
  • 0: 持有
  • 1: 卖出

利润 = Σ(strategyi × pricesi)

我们可以进行最多一次修改:选择连续 k 个元素,前 k/2 个改为 0,后 k/2 个改为 1。

求最大可能利润。

解题思路

方法一:暴力枚举(朴素思路)

最直接的想法是枚举所有可能的修改位置:

  • 不修改的情况
  • 从索引 0, 1, 2, ..., n-k 开始修改

每次计算修改后的总利润,取最大值。

时间复杂度: O(n²) - 枚举 O(n) 个位置,每次重新计算总利润 O(n)

方法二:前缀和优化(推荐)

观察到暴力方法有大量重复计算。我们可以用前缀和优化。

核心思想

设原始利润为 base = Σ(strategy[i] × prices[i])

当我们修改从位置 i 开始的 k 个元素时:

复制代码
修改前: [i, i+1, ..., i+k/2-1, i+k/2, ..., i+k-1]
修改后: [     全部变成0      ][    全部变成1     ]

设:

  • p1 = i, p2 = i + k/2 - 1(前半部分,变成 0)
  • p3 = i + k/2, p4 = i + k - 1(后半部分,变成 1)

则:

  • 原利润在 p1, p2 区间: base1 = Σ(strategy[j] × prices[j]),修改后变为 0
  • 原利润在 p3, p4 区间: base2 = Σ(strategy[j] × prices[j]),修改后变为 Σprices[j]

关键公式:

复制代码
新利润 = base - base1 - base2 + Σprices[p3~p4]

即:

复制代码
profit_diff = Σprices[p3~p4] - base1 - base2

只有当 profit_diff > 0 时,修改才能提升利润。

前缀和预处理

使用两个前缀和数组:

  1. prefixSumsi: prices 的前缀和,用于快速计算价格区间和
  2. baseSumsi: strategyj × pricesj 的前缀和,用于快速计算原利润

这样每次查询区间和的时间从 O(k) 降到 O(1)。

时间复杂度: O(n) - 预处理 O(n),枚举 O(n) 个位置,每次 O(1) 计算

空间复杂度: O(n)

代码实现

go 复制代码
func maxProfit(prices []int, strategy []int, k int) int64 {
    n := len(prices)
    
    // 前缀和预处理
    prefixSums := make([]int64, n+1)  // prices 的前缀和
    baseSums := make([]int64, n+1)    // strategy[i]*prices[i] 的前缀和
    var base int64
    
    for i := 0; i < n; i++ {
        prefixSums[i+1] = prefixSums[i] + int64(prices[i])
        base += int64(strategy[i]) * int64(prices[i])
        baseSums[i+1] = baseSums[i] + int64(strategy[i])*int64(prices[i])
    }
    
    // 边界:k > n 时无法修改
    if k > n {
        return base
    }
    
    maxProfit := base
    
    // 枚举所有修改起点
    for i := 0; i <= n-k; i++ {
        p1 := i
        p2 := i + k/2 - 1
        p3 := i + k/2
        p4 := i + k - 1
        
        // 原利润中被修改部分的贡献
        base1 := baseSums[p2+1] - baseSums[p1]
        base2 := baseSums[p4+1] - baseSums[p3]
        
        // 修改后后半部分的贡献(全为1)
        priceSum := prefixSums[p4+1] - prefixSums[p3]
        
        // 新利润 = 原利润 - 被移除部分 + 新增部分
        profit := base - base1 - base2 + priceSum
        
        if profit > maxProfit {
            maxProfit = profit
        }
    }
    
    return maxProfit
}

示例演示

以 prices = [4,2,8], strategy = [-1,0,1], k = 2 为例:

预处理:

  • base = (-1)×4 + 0×2 + 1×8 = 4
  • prefixSums = 0, 4, 6, 14
  • baseSums = 0, -4, -4, 4

枚举修改位置:

  • i=0: 修改 0,1 → 0,1,1

    • base1 = baseSums1 - baseSums0 = -4
    • base2 = 0 (p3=p4+1)
    • priceSum = prefixSums2 - prefixSums1 = 2
    • profit = 4 - (-4) - 0 + 2 = 10 ✓
  • i=1: 修改 1,2 → -1,0,1 (无变化)

    • profit = 4

最大利润: 10

复杂度分析

  • 时间复杂度: O(n),其中 n 是数组长度
  • 空间复杂度: O(n),用于存储前缀和数组

总结

本题的关键是识别暴力枚举中的重复计算,通过前缀和实现 O(1) 的区间和查询,将时间复杂度从 O(n²) 优化到 O(n)。这是一个典型的「暴力→优化」的思维过程。

相关推荐
weixin_307779134 小时前
C++代码实现MATLAB中的dlarray函数功能
开发语言·c++·算法·matlab
2601_949950638 小时前
练题簿在线免费刷题 从资料整理到考前自测
面试·职场和发展·pdf·word·刷题
hetao17338379 小时前
2026-10-03~04 hetao1733837 的刷题记录
c++·算法
Zootopia62610 小时前
飞行力学知识梳理1|飞行性能与稳定性
人工智能·python·算法·机器学习·无人机·学习方法·信息与通信
longlongzihan10 小时前
从LeetCode 70. 爬楼梯看动态规划:如何精准推导状态转移方程?
c++·算法·leetcode·动态规划
2601_9603563811 小时前
2027校招采购岗解析:数学基础如何迁移到需求预测、成本与供应商分析
人工智能·算法
h_a_o777oah11 小时前
【树论】树链剖分:将树形结构转化为线性结构以处理树上路径问题(洛谷 P3384 附C++代码模板)
数据结构·算法·线段树·acm·树链剖分·树论·重链剖分
HEJOO911 小时前
指针的算术运算详解
java·数据结构·算法
Coder-magician11 小时前
《代码随想录》刷题打卡day47:并查集-part01
数据结构·算法·图论
lsc_blog11 小时前
远程和居家办公的经历,技术简历上怎么摆
面试·职场和发展·重构·pdf·求职招聘