区间动态规划例题:带权重的区间调度问题(允许区间重叠的加权版本)
字数 5876 2025-12-24 19:02:37

区间动态规划例题:带权重的区间调度问题(允许区间重叠的加权版本)

题目描述

给定一系列任务,每个任务表示为区间 [start_i, end_i),以及该任务对应的权重(或价值)weight_i。你需要从中选择一个任务子集,允许任务区间重叠,但选择的子集必须满足一个约束条件:任何两个被选中的任务之间,其区间不能有超过 k 个重叠(这里我们定义“重叠”为两个任务区间有交集)。更形式化地,对于被选中的任意两个任务 ij,如果 start_i < end_jstart_j < end_i,则认为它们重叠。
目标:最大化所选任务的总权重之和。

举例:
假设有任务区间:
任务1: [1, 4), weight=5
任务2: [2, 5), weight=6
任务3: [3, 6), weight=4
任务4: [7, 10), weight=8
约束 k=2(即最多允许任意两个任务之间有重叠,但如果有三个任务两两重叠,则不允许)。
一种可能的选择是 {任务1, 任务2, 任务4},总权重=5+6+8=19(因为任务1和任务2重叠,但任务4不与其他重叠,满足 k=2,任意两个任务之间的重叠数不超过2)。但如果选择 {任务1, 任务2, 任务3},则这三个任务两两重叠,违反了 k=2(因为存在三个任务互相重叠,即重叠数超过2)。

解题思路

这是一个区间动态规划问题,我们需要考虑任务的选择顺序和重叠限制。
关键点:

  1. 将任务按照结束时间 end 排序,这样可以方便地判断任务之间的先后关系。
  2. 定义状态 dp[i] 表示考虑前 i 个任务(按结束时间排序后),并且必须选择第 i 个任务时,能达到的最大权重和。
  3. 状态转移时,我们需要找到前一个可以选择的最近任务 j,使得任务 j 与任务 i 不冲突(即 end_j <= start_i),但由于允许重叠,我们还需要考虑重叠限制。
    • 重叠限制意味着我们不能选择与当前任务 i 重叠的任务数量超过 k。但直接处理“任意两个任务之间的重叠”比较复杂,我们可以换一种思路:
      实际上,题目等价于:在任意时刻,被选中的任务中互相重叠的任务个数不能超过 k
      但更简单的转化是:我们可以将问题视为选择若干互不相交的任务组,每组内的任务两两重叠,且每组的大小不超过 k
      但这样转化后仍然难以直接DP。

    • 另一种更直接的DP方法:定义状态 dp[i][c] 表示考虑前 i 个任务,且以第 i 个任务作为一组重叠任务的最后一个,这一组重叠任务的数量为 c(1 ≤ c ≤ k)时的最大权重和。
      转移时,我们考虑前一个任务 j

      • 如果任务 j 与任务 i 不重叠(即 end_j <= start_i),那么任务 i 可以新开一组,即 c=1,从 dp[j][*] 转移过来。
      • 如果任务 j 与任务 i 重叠(即 end_j > start_i),那么任务 i 可以加入任务 j 所在的重叠组,但要求重叠组的大小不超过 k,即从 dp[j][c-1] 转移过来(c ≥ 2)。

      但这样的状态定义会导致复杂度较高(O(n² * k)),且需要考虑重叠组的连续性。

    • 更简洁的解法(本题的标准解法):
      我们先预处理出每个任务 i 的“前一个不重叠任务”的索引 prev[i],即最大的 j 满足 end_j <= start_i(二分查找即可)。
      然后,我们定义 dp[i] 表示考虑前 i 个任务(排序后)时,能获得的最大权重和,此时不要求必须选择第 i 个任务
      转移有两种选择:
      (1) 不选任务 i:dp[i] = dp[i-1]
      (2) 选任务 i:那么我们需要确保选择任务 i 后,与它重叠的任务数不超过 k。
      如何判断?我们可以枚举一个位置 p,使得从 pi 的所有任务都与任务 i 重叠(即 start_p < end_i),并且选择任务 i 时,这些重叠的任务中最多只能选 k 个(包括 i 本身)。
      但这样枚举太耗时。

      实际上,我们可以将问题转化为:
      选择若干个任务,使得在任意时刻,被选中的任务中,结束时间晚于当前时刻、开始时间早于当前时刻的任务数不超过 k
      这个条件等价于:在时间轴上滑动,同时维护一个“选中任务集合”,这个集合在任意时刻的大小不超过 k。

      但区间DP通常按区间端点进行划分。更巧妙的解法是:
      将任务按结束时间排序后,用 dp[i][j] 表示考虑前 i 个任务,且选中了 j 个任务时的最大权重和,其中 j ≤ k?不对,因为 k 是重叠限制,不是选中任务数的限制。

      经过思考,我发现这个问题实际上是一个经典的带权区间调度问题,但允许有限重叠(k-overlap)。
      标准解法是:

      1. 将任务按结束时间排序。
      2. 定义 dp[i] 为前 i 个任务的最大权重和。
      3. 对于每个任务 i,我们考虑两种情况:
        • 不选 i:dp[i] = dp[i-1]
        • 选 i:那么我们需要找到前一个与 i 不冲突的任务集合。但由于允许重叠,我们可以选择 i,并且同时选择所有与 i 重叠的任务,只要这些重叠的任务不超过 k 个。
          但这样会变成组合问题。

      实际上,这个问题有一个更简单的定义:
      选择一组任务,使得没有超过 k 个任务在任意时间点同时重叠
      这等价于:将时间轴上的每个点,被覆盖的次数不超过 k。

      对于此类问题,常用的方法是转化为最大权 k-区间包问题。
      一个高效的DP解法:

      1. 将所有区间按右端点排序。
      2. 定义 dp[i][j] 表示考虑前 i 个区间,且选择了 j 个区间时的最大权重和(j ≤ 总任务数)。
      3. 转移:
        • 不选第 i 个区间:dp[i][j] = dp[i-1][j]
        • 选第 i 个区间:那么我们需要找到最后一个与第 i 个区间不重叠的区间 p(即 end_p ≤ start_i)。
          如果选择第 i 个区间,那么我们可以从 dp[p][j-1] 转移过来,但前提是我们必须保证加入第 i 个区间后,不会造成在某些时间点上重叠的区间数超过 k。
          如何保证?实际上,如果我们按结束时间排序后选择区间,并且保证选择的区间集合中,任意两个重叠的区间之间满足某种性质,就可以用“最大独立集”的扩展来解。

      鉴于这个问题的复杂性,我决定采用一种更直观且易于实现的DP思路:
      将任务按开始时间排序,然后定义状态 dp[i] 表示以第 i 个任务为最后一个被选中的任务时的最大权重和。
      转移时,我们枚举前一个被选中的任务 j,要求任务 j 与任务 i 不重叠(即 end_j ≤ start_i),并且从 j 到 i 之间被跳过的任务中,与 i 重叠的任务数不超过 k-1(因为加上 i 本身,重叠数不超过 k)。
      但这样仍然需要判断重叠数。

      最终,我采用以下方法(标准解法):

      1. 将任务按结束时间排序。
      2. 对于每个任务 i,预处理出 prev[i] 表示最大的 j 满足 end_j ≤ start_i(即不重叠的前一个任务)。
      3. 定义 dp[i][t] 表示考虑前 i 个任务,且在最后一个被选中的任务结束之后,又额外选择了 t 个与当前任务重叠的任务(t ≤ k-1)时的最大权重和。
        这个状态定义比较巧妙,它记录了重叠任务的数量。
        转移时:
        • 不选任务 i:dp[i][t] = dp[i-1][t]
        • 选任务 i:那么我们需要找到前一个不重叠的任务 p = prev[i],然后从 dp[p][*] 转移过来,同时,如果 t > 0,则说明有重叠任务被选择。

      然而,这种状态定义仍较复杂。

      为了清晰起见,我决定采用一种简化但能说明区间DP思想的版本:
      假设 k=1,即不允许任何重叠。这就是经典的加权区间调度问题。
      状态定义:dp[i] 表示考虑前 i 个任务(按结束时间排序)时的最大权重和。
      转移:
      dp[i] = max(dp[i-1], dp[prev[i]] + weight_i)
      其中 prev[i] 是最大的 j 满足 end_j ≤ start_i

      对于 k>1 的情况,我们可以扩展状态:
      dp[i][c] 表示考虑前 i 个任务,且以第 i 个任务作为一组重叠任务的最后一个,这组重叠任务包含 c 个任务(1 ≤ c ≤ k)时的最大权重和。
      转移:
      dp[i][c] = max_{j < i} { if (end_j ≤ start_i) then dp[j][*] + weight_i # 新开一组,c=1 else if (c > 1) then dp[j][c-1] + weight_i # 加入重叠组 }
      同时,我们还需要一个全局最优解数组 best[i] 表示前 i 个任务的最大权重和(不一定以 i 结尾)。

      实现时,我们按结束时间排序后,对于每个 i,枚举所有 j < i,根据是否重叠决定转移。时间复杂度 O(n² * k)。

逐步讲解

我们以 k=2 为例,任务区间如下(已按结束时间排序):
任务1: [1, 4), w=5
任务2: [2, 5), w=6
任务3: [3, 6), w=4
任务4: [7, 10), w=8

步骤1:预处理
对于每个任务 i,计算 prev[i] 表示最后一个不与 i 重叠的任务索引(即最大的 j 满足 end_j ≤ start_i)。

  • 任务1: start=1, prev[1]=0(虚拟任务0,表示空)
  • 任务2: start=2, 检查任务1的end=4,4>2,所以重叠,prev[2]=0
  • 任务3: start=3, 检查任务2的end=5>3,任务1的end=4>3,所以重叠,prev[3]=0
  • 任务4: start=7, 检查任务3的end=6≤7,所以prev[4]=3

步骤2:状态定义
dp[i][c]:考虑前 i 个任务,且以任务 i 作为一组重叠任务的最后一个,这组重叠任务包含 c 个任务(1≤c≤k)时的最大权重和。
best[i]:考虑前 i 个任务时的全局最大权重和(不一定以 i 结尾)。

初始化:dp[*][*] = -∞best[0]=0

步骤3:状态转移
按 i=1 到 n 顺序计算:

  • i=1(任务1):

    • c=1:新开一组,可以从虚拟任务0转移:dp[1][1] = weight1 = 5
    • c=2:不可能,因为c=2需要前一个任务与它重叠,但前面没有任务。
    • best[1] = max(best[0], dp[1][1]) = max(0,5)=5
  • i=2(任务2):

    • c=1:新开一组,可以从 best[prev[2]] 转移,prev[2]=0,所以 dp[2][1] = best[0] + weight2 = 0+6=6
      也可以从任务1转移,但任务1与任务2重叠(end1=4 > start2=2),所以不能作为不重叠的前驱。
    • c=2:加入重叠组,需要前一个任务与它重叠(即任务1),且该组已有c-1=1个任务。
      所以从 dp[1][1] 转移:dp[2][2] = dp[1][1] + weight2 = 5+6=11
    • best[2] = max(best[1], dp[2][1], dp[2][2]) = max(5,6,11)=11
  • i=3(任务3):

    • c=1:新开一组,从 best[prev[3]] 转移,prev[3]=0,所以 dp[3][1] = best[0] + weight3 = 0+4=4
    • c=2:加入重叠组,需要前一个重叠任务(任务1或任务2)。
      从任务1:dp[1][1] + weight3 = 5+4=9
      从任务2:dp[2][1] + weight3 = 6+4=10
      取最大值:dp[3][2] = 10
    • c=3?但k=2,所以c不能超过2。
    • best[3] = max(best[2], dp[3][1], dp[3][2]) = max(11,4,10)=11
  • i=4(任务4):

    • c=1:新开一组,从 best[prev[4]] 转移,prev[4]=3,所以 dp[4][1] = best[3] + weight4 = 11+8=19
    • c=2:加入重叠组,但任务4与任务3不重叠(end3=6 ≤ start4=7),所以不能从任务3转移。实际上,任务4与前面所有任务都不重叠,所以c=2不可能。
    • best[4] = max(best[3], dp[4][1]) = max(11,19)=19

最终答案:best[4]=19

验证:选择任务1、2、4,权重和=5+6+8=19,且任务1和任务2重叠(重叠任务数为2,满足k=2),任务4不与其他重叠,符合条件。

复杂度分析

  • 预处理 prev:排序 O(n log n),二分查找每个任务的 prev 需 O(log n),总 O(n log n)。
  • DP状态数:O(n * k)。
  • 转移:对于每个 dp[i][c],c=1时从 best[prev[i]] 转移(O(1)),c>1时需要枚举所有与 i 重叠的前驱任务 j(最坏 O(n)),因此总复杂度 O(n² * k)。
    可以通过进一步优化(如对于每个 i,只考虑与它重叠的前驱)来减少枚举,但最坏仍为 O(n² * k)。

总结

这个题目是带权区间调度问题的扩展,允许有限重叠(k-overlap)。核心思路是按结束时间排序,定义状态 dp[i][c] 表示以任务 i 为结尾且所在重叠组大小为 c 时的最大权重,同时维护全局最优 best[i]。转移时分别考虑新开一组(c=1)和加入前驱的重叠组(c>1)两种情况。最终答案为 best[n]

通过这个例子,你可以看到区间DP如何通过增加状态维度(重叠组大小 c)来应对更复杂的约束条件(有限重叠)。

区间动态规划例题:带权重的区间调度问题(允许区间重叠的加权版本) 题目描述 给定一系列任务,每个任务表示为区间 [start_i, end_i) ,以及该任务对应的权重(或价值) weight_i 。你需要从中选择一个任务子集, 允许任务区间重叠 ,但选择的子集必须满足一个约束条件: 任何两个被选中的任务之间,其区间不能有超过 k 个重叠 (这里我们定义“重叠”为两个任务区间有交集)。更形式化地,对于被选中的任意两个任务 i 和 j ,如果 start_i < end_j 且 start_j < end_i ,则认为它们重叠。 目标:最大化所选任务的总权重之和。 举例: 假设有任务区间: 任务1: [ 1, 4), weight=5 任务2: [ 2, 5), weight=6 任务3: [ 3, 6), weight=4 任务4: [ 7, 10), weight=8 约束 k=2 (即最多允许任意两个任务之间有重叠,但如果有三个任务两两重叠,则不允许)。 一种可能的选择是 {任务1, 任务2, 任务4},总权重=5+6+8=19(因为任务1和任务2重叠,但任务4不与其他重叠,满足 k=2 ,任意两个任务之间的重叠数不超过2)。但如果选择 {任务1, 任务2, 任务3},则这三个任务两两重叠,违反了 k=2 (因为存在三个任务互相重叠,即重叠数超过2)。 解题思路 这是一个区间动态规划问题,我们需要考虑任务的选择顺序和重叠限制。 关键点: 将任务按照结束时间 end 排序,这样可以方便地判断任务之间的先后关系。 定义状态 dp[i] 表示考虑前 i 个任务(按结束时间排序后),并且 必须选择第 i 个任务 时,能达到的最大权重和。 状态转移时,我们需要找到前一个可以选择的最近任务 j ,使得任务 j 与任务 i 不冲突(即 end_j <= start_i ),但由于允许重叠,我们还需要考虑重叠限制。 重叠限制意味着我们不能选择与当前任务 i 重叠的任务数量超过 k 。但直接处理“任意两个任务之间的重叠”比较复杂,我们可以换一种思路: 实际上,题目等价于:在任意时刻,被选中的任务中 互相重叠的任务个数 不能超过 k 。 但更简单的转化是:我们可以将问题视为 选择若干互不相交的任务组,每组内的任务两两重叠,且每组的大小不超过 k 。 但这样转化后仍然难以直接DP。 另一种更直接的DP方法:定义状态 dp[i][c] 表示考虑前 i 个任务,且 以第 i 个任务作为一组重叠任务的最后一个 ,这一组重叠任务的数量为 c (1 ≤ c ≤ k)时的最大权重和。 转移时,我们考虑前一个任务 j : 如果任务 j 与任务 i 不重叠(即 end_j <= start_i ),那么任务 i 可以新开一组,即 c=1 ,从 dp[j][*] 转移过来。 如果任务 j 与任务 i 重叠(即 end_j > start_i ),那么任务 i 可以加入任务 j 所在的重叠组,但要求重叠组的大小不超过 k ,即从 dp[j][c-1] 转移过来(c ≥ 2)。 但这样的状态定义会导致复杂度较高(O(n² * k)),且需要考虑重叠组的连续性。 更简洁的解法 (本题的标准解法): 我们先预处理出每个任务 i 的“前一个不重叠任务”的索引 prev[i] ,即最大的 j 满足 end_j <= start_i (二分查找即可)。 然后,我们定义 dp[i] 表示考虑前 i 个任务(排序后)时,能获得的最大权重和, 此时不要求必须选择第 i 个任务 。 转移有两种选择: (1) 不选任务 i: dp[i] = dp[i-1] (2) 选任务 i:那么我们需要确保选择任务 i 后,与它重叠的任务数不超过 k。 如何判断?我们可以枚举一个位置 p ,使得从 p 到 i 的所有任务都与任务 i 重叠(即 start_p < end_i ),并且选择任务 i 时,这些重叠的任务中最多只能选 k 个(包括 i 本身)。 但这样枚举太耗时。 实际上,我们可以将问题转化为: 选择若干个任务,使得在任意时刻,被选中的任务中,结束时间晚于当前时刻、开始时间早于当前时刻的任务数不超过 k 。 这个条件等价于:在时间轴上滑动,同时维护一个“选中任务集合”,这个集合在任意时刻的大小不超过 k。 但区间DP通常按区间端点进行划分。更巧妙的解法是: 将任务按结束时间排序后,用 dp[i][j] 表示考虑前 i 个任务,且选中了 j 个任务时的最大权重和 ,其中 j ≤ k?不对,因为 k 是重叠限制,不是选中任务数的限制。 经过思考,我发现这个问题实际上是一个经典的 带权区间调度问题 ,但允许有限重叠(k-overlap)。 标准解法是: 将任务按结束时间排序。 定义 dp[i] 为前 i 个任务的最大权重和。 对于每个任务 i,我们考虑两种情况: 不选 i: dp[i] = dp[i-1] 选 i:那么我们需要找到前一个与 i 不冲突的任务集合。但由于允许重叠,我们可以选择 i,并且同时选择所有与 i 重叠的任务,只要这些重叠的任务不超过 k 个。 但这样会变成组合问题。 实际上,这个问题有一个更简单的定义: 选择一组任务,使得没有超过 k 个任务在任意时间点同时重叠 。 这等价于:将时间轴上的每个点,被覆盖的次数不超过 k。 对于此类问题,常用的方法是转化为 最大权 k-区间包 问题。 一个高效的DP解法: 将所有区间按右端点排序。 定义 dp[i][j] 表示考虑前 i 个区间,且选择了 j 个区间时的最大权重和(j ≤ 总任务数)。 转移: 不选第 i 个区间: dp[i][j] = dp[i-1][j] 选第 i 个区间:那么我们需要找到最后一个与第 i 个区间不重叠的区间 p(即 end_p ≤ start_i )。 如果选择第 i 个区间,那么我们可以从 dp[p][j-1] 转移过来,但前提是我们必须保证加入第 i 个区间后,不会造成在某些时间点上重叠的区间数超过 k。 如何保证?实际上,如果我们按结束时间排序后选择区间,并且保证选择的区间集合中,任意两个重叠的区间之间满足某种性质,就可以用“最大独立集”的扩展来解。 鉴于这个问题的复杂性,我决定采用一种更直观且易于实现的DP思路: 将任务按开始时间排序 ,然后定义状态 dp[i] 表示以第 i 个任务为最后一个被选中的任务时的最大权重和。 转移时,我们枚举前一个被选中的任务 j,要求任务 j 与任务 i 不重叠(即 end_j ≤ start_i ),并且 从 j 到 i 之间被跳过的任务中,与 i 重叠的任务数不超过 k-1 (因为加上 i 本身,重叠数不超过 k)。 但这样仍然需要判断重叠数。 最终,我采用以下方法(标准解法): 将任务按结束时间排序。 对于每个任务 i,预处理出 prev[i] 表示最大的 j 满足 end_j ≤ start_i (即不重叠的前一个任务)。 定义 dp[i][t] 表示考虑前 i 个任务,且 在最后一个被选中的任务结束之后,又额外选择了 t 个与当前任务重叠的任务 (t ≤ k-1)时的最大权重和。 这个状态定义比较巧妙,它记录了重叠任务的数量。 转移时: 不选任务 i: dp[i][t] = dp[i-1][t] 选任务 i:那么我们需要找到前一个不重叠的任务 p = prev[i] ,然后从 dp[p][*] 转移过来,同时,如果 t > 0,则说明有重叠任务被选择。 然而,这种状态定义仍较复杂。 为了清晰起见,我决定采用一种简化但能说明区间DP思想的版本: 假设 k=1 ,即不允许任何重叠。这就是经典的加权区间调度问题。 状态定义: dp[i] 表示考虑前 i 个任务(按结束时间排序)时的最大权重和。 转移: dp[i] = max(dp[i-1], dp[prev[i]] + weight_i) 其中 prev[i] 是最大的 j 满足 end_j ≤ start_i 。 对于 k>1 的情况,我们可以扩展状态: dp[i][c] 表示考虑前 i 个任务,且 以第 i 个任务作为一组重叠任务的最后一个 ,这组重叠任务包含 c 个任务(1 ≤ c ≤ k)时的最大权重和。 转移: dp[i][c] = max_{j < i} { if (end_j ≤ start_i) then dp[j][*] + weight_i # 新开一组,c=1 else if (c > 1) then dp[j][c-1] + weight_i # 加入重叠组 } 同时,我们还需要一个全局最优解数组 best[i] 表示前 i 个任务的最大权重和(不一定以 i 结尾)。 实现时,我们按结束时间排序后,对于每个 i,枚举所有 j < i,根据是否重叠决定转移。时间复杂度 O(n² * k)。 逐步讲解 我们以 k=2 为例,任务区间如下(已按结束时间排序): 任务1: [ 1, 4), w=5 任务2: [ 2, 5), w=6 任务3: [ 3, 6), w=4 任务4: [ 7, 10), w=8 步骤1:预处理 对于每个任务 i,计算 prev[i] 表示最后一个不与 i 重叠的任务索引(即最大的 j 满足 end_j ≤ start_i )。 任务1: start=1, prev[ 1 ]=0(虚拟任务0,表示空) 任务2: start=2, 检查任务1的end=4,4>2,所以重叠,prev[ 2 ]=0 任务3: start=3, 检查任务2的end=5>3,任务1的end=4>3,所以重叠,prev[ 3 ]=0 任务4: start=7, 检查任务3的end=6≤7,所以prev[ 4 ]=3 步骤2:状态定义 dp[i][c] :考虑前 i 个任务,且以任务 i 作为一组重叠任务的最后一个,这组重叠任务包含 c 个任务(1≤c≤k)时的最大权重和。 best[i] :考虑前 i 个任务时的全局最大权重和(不一定以 i 结尾)。 初始化: dp[*][*] = -∞ , best[0]=0 。 步骤3:状态转移 按 i=1 到 n 顺序计算: i=1(任务1): c=1:新开一组,可以从虚拟任务0转移: dp[1][1] = weight1 = 5 c=2:不可能,因为c=2需要前一个任务与它重叠,但前面没有任务。 best[1] = max(best[0], dp[1][1]) = max(0,5)=5 i=2(任务2): c=1:新开一组,可以从 best[prev[2]] 转移,prev[ 2]=0,所以 dp[2][1] = best[0] + weight2 = 0+6=6 也可以从任务1转移,但任务1与任务2重叠(end1=4 > start2=2),所以不能作为不重叠的前驱。 c=2:加入重叠组,需要前一个任务与它重叠(即任务1),且该组已有c-1=1个任务。 所以从 dp[1][1] 转移: dp[2][2] = dp[1][1] + weight2 = 5+6=11 best[2] = max(best[1], dp[2][1], dp[2][2]) = max(5,6,11)=11 i=3(任务3): c=1:新开一组,从 best[prev[3]] 转移,prev[ 3]=0,所以 dp[3][1] = best[0] + weight3 = 0+4=4 c=2:加入重叠组,需要前一个重叠任务(任务1或任务2)。 从任务1: dp[1][1] + weight3 = 5+4=9 从任务2: dp[2][1] + weight3 = 6+4=10 取最大值: dp[3][2] = 10 c=3?但k=2,所以c不能超过2。 best[3] = max(best[2], dp[3][1], dp[3][2]) = max(11,4,10)=11 i=4(任务4): c=1:新开一组,从 best[prev[4]] 转移,prev[ 4]=3,所以 dp[4][1] = best[3] + weight4 = 11+8=19 c=2:加入重叠组,但任务4与任务3不重叠(end3=6 ≤ start4=7),所以不能从任务3转移。实际上,任务4与前面所有任务都不重叠,所以c=2不可能。 best[4] = max(best[3], dp[4][1]) = max(11,19)=19 最终答案: best[4]=19 。 验证 :选择任务1、2、4,权重和=5+6+8=19,且任务1和任务2重叠(重叠任务数为2,满足k=2),任务4不与其他重叠,符合条件。 复杂度分析 预处理 prev :排序 O(n log n),二分查找每个任务的 prev 需 O(log n),总 O(n log n)。 DP状态数:O(n * k)。 转移:对于每个 dp[i][c] ,c=1时从 best[prev[i]] 转移(O(1)),c>1时需要枚举所有与 i 重叠的前驱任务 j(最坏 O(n)),因此总复杂度 O(n² * k)。 可以通过进一步优化(如对于每个 i,只考虑与它重叠的前驱)来减少枚举,但最坏仍为 O(n² * k)。 总结 这个题目是带权区间调度问题的扩展,允许有限重叠(k-overlap)。核心思路是按结束时间排序,定义状态 dp[i][c] 表示以任务 i 为结尾且所在重叠组大小为 c 时的最大权重,同时维护全局最优 best[i] 。转移时分别考虑新开一组(c=1)和加入前驱的重叠组(c>1)两种情况。最终答案为 best[n] 。 通过这个例子,你可以看到区间DP如何通过增加状态维度(重叠组大小 c)来应对更复杂的约束条件(有限重叠)。