算法——关于区间重叠的几个问题

 

区间重叠的问题:给定包含起始时间s和终止时间t(s < t) 的n个区间段,依据区间重叠情况衍生出来的一系列问题,一般以会议室的安排为外壳作为题目,给定n个会议的开始和结束时间,求相关问题,输入格式为:

n
s1 t1
s2 t2
...
sn tn

以下整理了三种常见题目:

1、最少会议室(最多重叠区间数)

求满足所有会议安排的最少会议室数量。即最多的相互重叠的区间数量。

 

如上图分析可知,可将所有时间点排序,初始化ans=0,遇到起始点+1,遇到终止点-1,即可得到任意区间段的区间重叠数量。

特殊情况:当同一区间点同时为起始点和终止点时(图中+0点),需将终止点标记排在起始点之前,即先-1再+1,否则会出现错误。

时间复杂度:时间主要耗费在排序2n个元素上,复杂度为O(nlogn)

基于C++的示例代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 bool cmp(const pair<int, int> x, const pair<int, int> y){
 6     // 对于相同的区间点,使终点始终在起点之前 
 7     if(x.first == y.first) return x.second > y.second;
 8     return x.first < y.first;
 9 }
10 
11 int main(){
12     int n, s, t;
13     vector<pair<int, int> > vals;
14     cin >> n;
15     
16     for(int i = 0; i < n; i++){
17         cin >> s >> t;
18         vals.push_back(pair<int, int>(s, 0));
19         vals.push_back(pair<int, int>(t, 1));
20     }
21     sort(vals.begin(), vals.end(), cmp);
22     
23     int K = 0, ans = 0;
24     for(int i = 0; i < vals.size(); i++){
25         // 遇到起点+1 
26         if(vals[i].second == 0){
27             K++;
28             ans = ans > K ? ans : K;
29         }
30         // 遇到终点-1 
31         else K--;
32     }
33     cout << ans << endl;
34     return 0;
35 }

2、最多数量会议安排(最多无重叠的区间数量)

在仅有一个会议室的情况下,求最多能满足的会议数量。即最多的无相互重叠的区间数量。

如上图所示,可用贪心法解决,按照会议结束时间排序,首先取第一个结束的会议,并记录结束时间end_t(图中红色点),依次遍历后续会议,若某会议的开始时间不小于end_t(图中蓝色点),则可安排该会议,同时更新end_t。

特殊情况:当结束时间相等时,开始时间的顺序其实无关紧要,如上图第三四行,两个会议均可,且安排后end_t相同,对后续安排不影响;若同时要求优先安排较长时间的会议、或者保证会议室总使用时间较长,可以在结束时间相等的情况下,使开始时间升序排列。

时间复杂度:时间主要耗费在排序n个元素上,复杂度为O(nlogn)

基于C++的示例代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 bool cmp(const pair<int, int> x, const pair<int, int> y){
 6     // 对于结束时间相同的,按起始时间升序排列
 7     if(x.second == y.second) return x.first < y.first;
 8     // 按结束时间升序排列 
 9     return x.second < y.second;
10 }
11 
12 int main(){
13     int n, s, t;
14     vector<pair<int, int> > vals;
15     cin >> n;
16     
17     for(int i = 0; i < n; i++){
18         cin >> s >> t;
19         vals.push_back(pair<int, int>(s, t));
20     }
21     sort(vals.begin(), vals.end(), cmp);
22     
23     int end_t = -1, ans = 0;
24     for(int i = 0; i < vals.size(); i++){
25         // 若当前会议开始时间不小于上一个安排会议的结束时间,则可安排 
26         if(vals[i].first >= end_t){
27             ans++;
28             end_t = vals[i].second;
29         }
30     }
31     cout << ans << endl;
32     return 0;
33 }

3、最长时间会议安排(最多无重叠的区间长度)

在仅有一个会议室的情况下,求最长的能满足的会议时间。即最多无相互重叠的区间长度。

该问题用动态规划解决,首先将会议按结束时间排序dp[i]表示以安排第i个会议作为最后一个会议时前i个会议能满足的最长时间,递推关系为dp[i] = (ti - si) + MAX{dp[j]},其中0<=j<i且si >= tj

特殊情况:对于结束时间相等的会议,按照开始时间顺序排序。

时间复杂度:排序n个元素复杂度为O(nlogn),动态规划复杂度为O(n2),整体复杂度为O(n2)

基于C++的示例代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <vector>
 4 using namespace std;
 5 
 6 bool cmp(const pair<int, int> x, const pair<int, int> y){ 
 7     // 对于结束时间相同的,按起始时间升序排列
 8     if(x.second == y.second) return x.first < y.first;
 9     // 按结束时间升序排列
10     return x.second < y.second;
11 }
12 
13 int main(){
14     int n, s, t;
15     vector<pair<int, int> > vals;
16     cin >> n;
17 
18     for(int i = 0; i < n; i++){
19         cin >> s >> t;
20         vals.push_back(pair<int, int>(s, t));
21     }   
22     sort(vals.begin(), vals.end(), cmp);
23 
24     vector<int> dp(n, 0); 
25     int ans = 0;
26     for(int i = 0; i < vals.size(); i++){
27         int mmax = 0;
28         for(int j = 0; j < i; j++){
29             // mmax = max{dp[j]}, for all 0 <= j < i and si >= tj
30             if((vals[i].first >= vals[j].second) && (dp[j] > mmax)){
31                 mmax = dp[j];
32             }
33         }
34         // dp[i] = (ti - si) + max{dp[j]}
35         dp[i] = vals[i].second - vals[i].first + mmax;
36         ans = ans > dp[i] ? ans : dp[i];
37     }   
38     cout << ans << endl;
39     return 0;
40 }
posted @ 2020-08-02 12:39  huochaitiantang  阅读(4327)  评论(1编辑  收藏  举报