区间树中的最大非重叠区间

Posted

技术标签:

【中文标题】区间树中的最大非重叠区间【英文标题】:Maximum non-overlapping intervals in a interval tree 【发布时间】:2013-11-08 02:31:22 【问题描述】:

给定一个时间间隔列表,我需要找到一组最大不重叠间隔。

例如,

如果我们有以下间隔:

[0600, 0830], [0800, 0900], [0900, 1100], [0900, 1130], 
[1030, 1400], [1230, 1400]

还规定时间必须在[0000, 2400] 范围内。

最大不重叠的间隔集是[0600, 0830], [0900, 1130], [1230, 1400]

我了解最大集包装是 NP-Complete。我想确认我的问题(间隔仅包含开始和结束时间)是否也是 NP-Complete。

如果是这样,有没有办法在指数时间内找到最佳解决方案,但需要更智能的预处理和修剪数据。或者如果有一个相对容易实现的固定参数易处理算法。我不想使用近似算法。

【问题讨论】:

“最大”是指间隔的最大个数还是间隔的最长总持续时间?您的示例解决方案是 3 个间隔,总持续时间为 6.5 小时。是什么让它最大,3 还是 6.5? 【参考方案1】:

这不是一个 NP 完全问题。我可以想到一个O(n * log(n))算法使用动态规划来解决这个问题。

假设我们有 n 个区间。假设给定的范围是S(在你的情况下是S = [0000, 2400])。要么假设所有区间都在S 内,要么在线性时间内消除所有不在S 内的区间。

    预处理:

    按开始点对所有区间进行排序。假设我们得到一个包含 n 个区间的数组A[n]。 这一步需要O(n * log(n))时间 对于区间的所有终点,找出其后最小起点的索引。假设我们得到一个 Next[n] 的数组 n 整数。 如果对于区间i, 的终点不存在这样的起点,我们可以将n 分配给Next[i]。 我们可以在O(n * log(n))时间通过枚举所有区间的n个端点来做到这一点,并使用二进制搜索来找到答案。也许存在线性方法来解决这个问题,但这没关系,因为上一步已经花费了O(n * log(n))时间。

    DP:

    假设[A[i].begin, S.end] 范围内的最大非重叠间隔为f[i]。那么f[0]就是我们想要的答案。 还假设f[n] = 0; 状态转移方程: f[i] = maxf[i+1], 1 + f[Next[i]] 很明显,DP 步骤需要线性时间。

上面的解决方案是我第一眼看到问题时想出的解决方案。之后,我还想出了一个更简单(但在大 O 表示法意义上并不快)的贪婪方法:

(使用与上述 DP 方法相同的符号和假设)

    预处理:按所有区间的结束点排序。假设我们得到一个包含 n 个区间的数组B[n]

    贪婪:

    int ans = 0, cursor = S.begin;
    for(int i = 0; i < n; i++)
        if(B[i].begin >= cursor)
            ans++;
            cursor = B[i].end;
        
    
    

以上两种解决方案都是我想到的,但是你的问题也称为活动选择问题,可以在***http://en.wikipedia.org/wiki/Activity_selection_problem找到。

另外,算法简介在 16.1 中深入讨论了这个问题。

【讨论】:

我对第 1 点的第二个项目符号有点困惑。你是说我们需要为下一个可能的间隔创建一个新数组吗?这是Next[n] 还是Next[n]==A[n]?也许你可以再写一些代码来澄清一下?

以上是关于区间树中的最大非重叠区间的主要内容,如果未能解决你的问题,请参考以下文章

重叠区间的最大子集数

使用间隔树的最大间隔重叠[关闭]

1133 不重叠的线段 (贪心算法,最大区间不重合问题)

区间的最大重叠次数

51Nod 1091 线段重叠 贪心 区间重叠

区间树算法,支持不重叠的区间合并