从两个区间到多笔交易:一道算法题的进化之旅

本文从一个简单的区间选择问题出发,逐步扩展其难度与适用范围,整体思路由浅入深,层层递进,尝试揭示背后的通用算法设计方法。


问题一:选择至多两个不重叠区间以最大化长度和

问题描述

给定 \(n\) 个左右端点 \([l_i, r_i],l_i\le r_i\),每个最终选择的区间需满足由某个左端点 \(l_x\) 与某个右端点 \(r_y\) 组成(\(y \ge x\)),要求从中选出至多两个两两不重叠的区间 \([start_i, end_i]\),使得总长度 \(\sum (end_i - start_i)\) 最大。

分析

\(2n\) 个端点,每个端点记录其类型(左端点或右端点)与对应的数值。为便于处理,我们按这些端点在数轴上的位置排序。由于只选择至多两个区间,我们可以枚举一个分割点,将区间划分为左右两部分,分别求得最大可选区间长度之和。为实现该策略,我们可以预处理两个数组:

  • MaxLeft[i]:表示从左到第 \(i\) 个点(含)中可以选出的最大单一区间长度;
  • MaxRight[i]:表示从第 \(i\) 个点(含)到右侧可以选出的最大单一区间长度。

遍历所有可能的分割点,取最大值:

\[\text{Result} = \max_i {\text{MaxLeft}[i] + \text{MaxRight}[i]} \]

维护 MaxLeft 的过程:若当前为左端点:$ \text{MaxLeft}[i] = \text{MaxLeft}[i - 1] $,同时更新最小左端点 \(MinL\);若当前为右端点:$ \text{MaxLeft}[i] = \max(\text{MaxLeft}[i - 1], \text{value}_i - MinL) $,即:

\[\text{MaxLeft}[i] = \begin{cases} \text{MaxLeft}[i-1] & \text{if } \text{type}_i = l \\ \max(\text{MaxLeft}[i-1], \text{value}_i - MinL) & \text{if } \text{type}_i = r \end{cases} \]

MaxRight 可类比从右向左更新,最终时间复杂度为 \(O(n)\)


问题二:选择至多 \(k\) 个不重叠区间以最大化长度和

问题描述

在问题一的基础上,将最多选取的区间个数拓展为 \(k\)。求最多选取 \(k\) 个两两不重叠的区间使得 \(\sum (end_i - start_i)\) 最大。

分析

此问题无法直接枚举分割点,需使用动态规划,定义状态:

  • \(dp[i][j]\) 表示前 \(i\) 个点中选出 \(j\) 个不重叠区间所能得到的最大值;
  • 边界条件为 \(dp[i][0] = 0, dp[0][j] = 0\)
  • 最终答案为 \(dp[2n][k]\)

状态转移

  1. 若当前为左端点:不可能构成新区间,直接继承前一状态: \(dp[i][j] = dp[i - 1][j]\)

  2. 若当前为右端点,有两种选择:

    • 不选当前节点:\(dp[i][j] = dp[i - 1][j]\)
    • 选当前节点,枚举所有之前的左端点 \(s\),更新为:\(dp[i][j] = \max(dp[i][j], dp[s - 1][j - 1] + \text{value}_i - \text{value}_s) \quad \text{if } \text{type}_s = l\)

转移方程汇总

\[dp[i][j] = \begin{cases} dp[i-1][j] & \text{if } \text{type}_i = l \\ \max \left( dp[i-1][j], \max_{\substack{1 \le s < i \ \text{type}_s = l}} (dp[s-1][j-1] + \text{value}_i - \text{value}_s) \right) & \text{if } \text{type}_i = r \end{cases} \]

初始实现时间复杂度为 \(O(n^2k)\),可优化查询,降至 \(O(nk \log n)\)


问题三:股票买卖问题与区间问题的关联

问题描述

  1. 给定数组 \(prices\),表示某支股票每天的价格。设计一个算法,最多完成 两笔 不重叠的买卖交易,求最大收益。
  2. 同样输入 \(prices\),现在最多完成 \(k\) 笔交易,求最大收益。

分析

这是 LeetCode 中经典问题:

本质是对价格序列寻找若干个不重叠的“低买高卖”区间。观察可知:

  • 任意一笔交易的有效区间应从局部最小值买入至局部最大值卖出;
  • \(prices[i+1] \le prices[i]\),则应跳过,因为买入不具优势。

据此可从原始 \(prices\) 序列提取出 \(m\) 个有效的上升区间对 \((l_i, r_i)\),满足 \(l_i < r_i\)\(r_i > l_{i+1}\),由此,原问题等价于从 \(m\) 个区间中选择最多 \(k\) 个两两不重叠区间使得 \(\sum (r_i - l_i)\) 最大,转化为问题一与问题二的形式,即可复用上述算法模型求解。

posted @ 2025-01-14 19:58  亦可九天揽月  阅读(14)  评论(0)    收藏  举报