带权重的最大不重叠区间子集选择问题
字数 3506 2025-12-21 17:48:55

带权重的最大不重叠区间子集选择问题


题目描述
给定一个区间集合,每个区间具有起始时间 start、结束时间 end 以及一个正权重 weight。如果两个区间的时间段重叠(包括端点重叠),则它们不能同时被选择。要求选择一个不重叠的区间子集,使得子集中所有区间的权重之和最大。

输入
一个列表 intervals,其中每个元素是三元组 (start, end, weight),且 start < end。区间数量 n 最多可能达到 10^5,但区间端点值范围可能较大。

输出
一个整数,表示最大可能的权重和。

示例
输入:intervals = [(1, 4, 2), (2, 5, 4), (6, 8, 3), (4, 7, 5)]
输出:8
解释:选择区间 (1, 4, 2)(6, 8, 3),权重和为 2 + 3 = 5?不对,实际上可以选 (2, 5, 4)(6, 8, 3) 得到 7,但最优是 (1, 4, 2)(4, 7, 5)?注意 (4, 7, 5)(1, 4, 2) 在端点 4 重叠吗?通常定义中若一个区间结束等于另一个区间开始,视为不重叠(即允许 end == start 不重叠)。若允许,则 (1, 4, 2)(4, 7, 5) 不重叠,权重和 2 + 5 = 7
但实际最大权重和是选择 (2, 5, 4)(6, 8, 3) 得 7?
我们仔细分析:

  • (1,4,2)
  • (2,5,4)
  • (6,8,3)
  • (4,7,5)
    不重叠组合:
  1. (1,4,2) 后,可接 (6,8,3),权重 5。
  2. (2,5,4) 后,可接 (6,8,3),权重 7。
  3. (4,7,5) 后,无法再接 (6,8,3)(重叠)。
  4. (6,8,3) 单独,权重 3。
    最大是 7。
    但题目要求最大权重和,可能还有其他组合:选 (4,7,5) 权重 5,不如 7。
    所以示例输出应为 7。
    我们再看一个更明显的例子:
    [(1, 3, 5), (2, 4, 6), (3, 5, 4), (5, 7, 3)]
    最优选 (1,3,5)(3,5,4)(5,7,3)?不,(1,3,5)(3,5,4) 不重叠(允许 end=start),可以同时选,且 (5,7,3) 也可接,权重 5+4+3=12。
    但若定义不允许 end=start 重叠,则 (1,3,5)(3,5,4) 不能同选,最优可能是 (2,4,6)(5,7,3) 得 9。

为简化,我们约定:若一个区间的 end 等于另一个区间的 start,视为不重叠(即区间是半开半闭 [start, end) 的表示)。
示例 [(1,4,2), (2,5,4), (6,8,3), (4,7,5)] 中,
(1,4)(4,7) 不重叠,可同选,权重 2+5=7。
(2,5)(4,7) 重叠(因为 (2,5) 包含时间点 4.5 与 (4,7) 重叠),不能同选。
最大权重是 (2,5,4)(6,8,3) 得 7?还是 (1,4,2)(4,7,5) 得 7?都是 7。
实际上,(4,7,5) 权重 5 比 (1,4,2) 的 2 大,选 (4,7,5) 后无法选 (6,8,3)(重叠),所以权重 5+? 只能单独 5,不如 7。
因此示例最大为 7。

题目要求设计一个算法,在 区间数量 n 较大(如 10^5)时也能高效求解


解题步骤

1. 问题建模
这是一个经典问题:加权区间调度(Weighted Interval Scheduling)。
每个区间 i 有 start[i], end[i], weight[i]
目标是选出权重和最大的不重叠区间子集。

2. 关键观察
如果所有区间按结束时间 end 从小到大排序,那么我们可以定义:
p(j) = 在区间 j 之前结束且不与 j 重叠的最后一个区间的索引(即最大的 i < j 使得 end[i] <= start[j])。
这样,对于区间 j,有两种选择:

  • 不选 j,则最优解是前 j-1 个区间的最优解。
  • 选 j,则最优解是 weight[j] + 最优解( p(j) )
    取两者最大值。

3. 动态规划定义
dp[i] 表示考虑前 i 个区间(按 end 排序后)能获得的最大权重和。
递推:

dp[i] = max( dp[i-1], weight[i] + dp[ p(i) ] )

其中 p(i) 如上定义。

4. 如何计算 p(i)
对每个区间 i,二分查找在它之前结束且不重叠的最后一个区间。
因为区间已按 end 排序,我们可以对每个 i,在区间 0..i-1 中二分查找 end[k] <= start[i] 的最大 k。

5. 算法流程

  1. 将所有区间按 end 升序排序。
  2. 计算 p[i] 数组(通过二分查找)。
  3. 初始化 dp[0] = 0(没有区间时权重 0),并虚拟一个区间 0 为占位,实际区间从 1 到 n 编号。
    更直接:dp[0] = weight[0] 的处理要小心,我们通常让 dp[i] 对应前 i 个区间(i从1开始)的最大权重和。
    定义 dp[0] = 0dp[1] = max(0, weight[1])?其实第一个区间可选可不选,但权重为正肯定选。
    更清晰:
    排序后区间索引 0..n-1。
    dp[i] 表示考虑前 i+1 个区间(即区间 0..i)的最大权重和。
    递推:
    dp[i] = max(dp[i-1], weight[i] + dp[p[i]]),其中 p[i] 是区间 i 的“前一个不重叠区间”的索引,若不存在则 p[i] = -1,此时 dp[-1] 视为 0。

6. 例子计算
示例:intervals = [(1, 4, 2), (2, 5, 4), (6, 8, 3), (4, 7, 5)]
按 end 排序:
索引 0: (1,4,2)
索引 1: (2,5,4)
索引 2: (4,7,5)
索引 3: (6,8,3)
计算 p[i]:
i=0: start=1,前面没有区间,p[0]=-1
i=1: start=2,查找 end<=2,区间 0 的 end=4>2,没有,p[1]=-1
i=2: start=4,查找 end<=4,区间 0 的 end=4<=4,区间 1 的 end=5>4,所以 p[2]=0
i=3: start=6,查找 end<=6,区间 2 的 end=7>6,区间 1 的 end=5<=6,所以 p[3]=1

dp 计算:
dp[-1]=0
i=0: dp[0] = max(dp[-1]=0, weight[0]=2 + dp[-1]=0) = max(0,2) = 2
i=1: dp[1] = max(dp[0]=2, weight[1]=4 + dp[-1]=0) = max(2,4) = 4
i=2: dp[2] = max(dp[1]=4, weight[2]=5 + dp[0]=2) = max(4,7) = 7
i=3: dp[3] = max(dp[2]=7, weight[3]=3 + dp[1]=4) = max(7,7) = 7
最终 dp[3]=7。

7. 时间复杂度
排序 O(n log n),计算 p[i] 需要 n 次二分查找 O(n log n),dp 计算 O(n)。
总 O(n log n),空间 O(n)。

8. 边界情况

  • 区间权重可能全为正,因此至少选一个区间(除非 n=0)。
  • 区间端点可能相等,按约定 end==start 不重叠。
  • 权重可能为 0,不影响算法。
  • n 很大时需注意内存和二分查找实现。

9. 扩展思考
如果要输出具体选择了哪些区间,可以回溯 dp 决策。
从 i=n-1 开始,若 dp[i] == dp[i-1] 说明未选 i,则 i--;否则选了 i,记录 i,跳转到 p[i] 继续。


这样,我们完成了带权重的最大不重叠区间子集选择问题的详细讲解。

带权重的最大不重叠区间子集选择问题 题目描述 给定一个区间集合,每个区间具有起始时间 start 、结束时间 end 以及一个正权重 weight 。如果两个区间的时间段重叠(包括端点重叠),则它们不能同时被选择。要求选择一个不重叠的区间子集,使得子集中所有区间的权重之和最大。 输入 一个列表 intervals ,其中每个元素是三元组 (start, end, weight) ,且 start < end 。区间数量 n 最多可能达到 10^5 ,但区间端点值范围可能较大。 输出 一个整数,表示最大可能的权重和。 示例 输入: intervals = [(1, 4, 2), (2, 5, 4), (6, 8, 3), (4, 7, 5)] 输出: 8 解释:选择区间 (1, 4, 2) 和 (6, 8, 3) ,权重和为 2 + 3 = 5 ?不对,实际上可以选 (2, 5, 4) 和 (6, 8, 3) 得到 7 ,但最优是 (1, 4, 2) 和 (4, 7, 5) ?注意 (4, 7, 5) 与 (1, 4, 2) 在端点 4 重叠吗?通常定义中若一个区间结束等于另一个区间开始,视为不重叠(即允许 end == start 不重叠)。若允许,则 (1, 4, 2) 和 (4, 7, 5) 不重叠,权重和 2 + 5 = 7 。 但实际最大权重和是选择 (2, 5, 4) 和 (6, 8, 3) 得 7? 我们仔细分析: (1,4,2) (2,5,4) (6,8,3) (4,7,5) 不重叠组合: 选 (1,4,2) 后,可接 (6,8,3) ,权重 5。 选 (2,5,4) 后,可接 (6,8,3) ,权重 7。 选 (4,7,5) 后,无法再接 (6,8,3) (重叠)。 选 (6,8,3) 单独,权重 3。 最大是 7。 但题目要求最大权重和,可能还有其他组合:选 (4,7,5) 权重 5,不如 7。 所以示例输出应为 7。 我们再看一个更明显的例子: [(1, 3, 5), (2, 4, 6), (3, 5, 4), (5, 7, 3)] 最优选 (1,3,5) 和 (3,5,4) 和 (5,7,3) ?不, (1,3,5) 与 (3,5,4) 不重叠(允许 end=start),可以同时选,且 (5,7,3) 也可接,权重 5+4+3=12。 但若定义不允许 end=start 重叠,则 (1,3,5) 与 (3,5,4) 不能同选,最优可能是 (2,4,6) 和 (5,7,3) 得 9。 为简化, 我们约定:若一个区间的 end 等于另一个区间的 start,视为不重叠 (即区间是半开半闭 [start, end) 的表示)。 示例 [(1,4,2), (2,5,4), (6,8,3), (4,7,5)] 中, (1,4) 与 (4,7) 不重叠,可同选,权重 2+5=7。 (2,5) 与 (4,7) 重叠(因为 (2,5) 包含时间点 4.5 与 (4,7) 重叠),不能同选。 最大权重是 (2,5,4) 和 (6,8,3) 得 7?还是 (1,4,2) 和 (4,7,5) 得 7?都是 7。 实际上, (4,7,5) 权重 5 比 (1,4,2) 的 2 大,选 (4,7,5) 后无法选 (6,8,3) (重叠),所以权重 5+? 只能单独 5,不如 7。 因此示例最大为 7。 题目要求设计一个算法,在 区间数量 n 较大(如 10^5)时也能高效求解 。 解题步骤 1. 问题建模 这是一个经典问题:加权区间调度(Weighted Interval Scheduling)。 每个区间 i 有 start[i] , end[i] , weight[i] 。 目标是选出权重和最大的不重叠区间子集。 2. 关键观察 如果所有区间按结束时间 end 从小到大排序,那么我们可以定义: p(j) = 在区间 j 之前结束且不与 j 重叠的最后一个区间的索引(即最大的 i < j 使得 end[i] <= start[j] )。 这样,对于区间 j,有两种选择: 不选 j,则最优解是前 j-1 个区间的最优解。 选 j,则最优解是 weight[j] + 最优解( p(j) ) 。 取两者最大值。 3. 动态规划定义 设 dp[i] 表示考虑前 i 个区间(按 end 排序后)能获得的最大权重和。 递推: 其中 p(i) 如上定义。 4. 如何计算 p(i) 对每个区间 i,二分查找在它之前结束且不重叠的最后一个区间。 因为区间已按 end 排序,我们可以对每个 i,在区间 0..i-1 中二分查找 end[k] <= start[i] 的最大 k。 5. 算法流程 将所有区间按 end 升序排序。 计算 p[i] 数组(通过二分查找)。 初始化 dp[0] = 0 (没有区间时权重 0),并虚拟一个区间 0 为占位,实际区间从 1 到 n 编号。 更直接: dp[0] = weight[0] 的处理要小心,我们通常让 dp[i] 对应前 i 个区间(i从1开始)的最大权重和。 定义 dp[0] = 0 , dp[1] = max(0, weight[1]) ?其实第一个区间可选可不选,但权重为正肯定选。 更清晰: 排序后区间索引 0..n-1。 dp[i] 表示考虑前 i+1 个区间(即区间 0..i)的最大权重和。 递推: dp[i] = max(dp[i-1], weight[i] + dp[p[i]]) ,其中 p[i] 是区间 i 的“前一个不重叠区间”的索引,若不存在则 p[i] = -1 ,此时 dp[-1] 视为 0。 6. 例子计算 示例: intervals = [(1, 4, 2), (2, 5, 4), (6, 8, 3), (4, 7, 5)] 按 end 排序: 索引 0: (1,4,2) 索引 1: (2,5,4) 索引 2: (4,7,5) 索引 3: (6,8,3) 计算 p[ i ]: i=0: start=1,前面没有区间,p[ 0 ]=-1 i=1: start=2,查找 end<=2,区间 0 的 end=4>2,没有,p[ 1 ]=-1 i=2: start=4,查找 end<=4,区间 0 的 end=4<=4,区间 1 的 end=5>4,所以 p[ 2 ]=0 i=3: start=6,查找 end<=6,区间 2 的 end=7>6,区间 1 的 end=5<=6,所以 p[ 3 ]=1 dp 计算: dp[ -1 ]=0 i=0: dp[ 0] = max(dp[ -1]=0, weight[ 0]=2 + dp[ -1 ]=0) = max(0,2) = 2 i=1: dp[ 1] = max(dp[ 0]=2, weight[ 1]=4 + dp[ -1 ]=0) = max(2,4) = 4 i=2: dp[ 2] = max(dp[ 1]=4, weight[ 2]=5 + dp[ 0 ]=2) = max(4,7) = 7 i=3: dp[ 3] = max(dp[ 2]=7, weight[ 3]=3 + dp[ 1 ]=4) = max(7,7) = 7 最终 dp[ 3 ]=7。 7. 时间复杂度 排序 O(n log n),计算 p[ i ] 需要 n 次二分查找 O(n log n),dp 计算 O(n)。 总 O(n log n),空间 O(n)。 8. 边界情况 区间权重可能全为正,因此至少选一个区间(除非 n=0)。 区间端点可能相等,按约定 end==start 不重叠。 权重可能为 0,不影响算法。 n 很大时需注意内存和二分查找实现。 9. 扩展思考 如果要输出具体选择了哪些区间,可以回溯 dp 决策。 从 i=n-1 开始,若 dp[ i] == dp[ i-1] 说明未选 i,则 i--;否则选了 i,记录 i,跳转到 p[ i ] 继续。 这样,我们完成了带权重的最大不重叠区间子集选择问题的详细讲解。