简单区间问题 选择不相交区间 区间选点 区间覆盖问题解答及代码 C++

1. 选择不相交区间
数轴上有n个开区间 ( a i , b i ) (a_i, b_i) (ai,bi)。选择尽量多个区间,使得这些区间没有公共点。

这是最简单的区间问题,很多区间问题都需要先排序,要么对 a i a_i ai排序,要么对 b i b_i bi排序。这道题直接将区间按照 b i b_i bi排序,第一个区间一定要选择,然后选择下一个符合条件的区间(不与第一个区间重叠),以此类推。

数据(5,7), (1,2), (4,6), (4,5), (0,3), (3,7), (8,10), (9,15), (16,17)。

#include
#include
#include
using namespace std;
vector<pair<int,int>> data {{5,7}, {1,2}, {4,6}, {4,5}, {0,3}, {3,7}, {8,10}, {9,15}, {16,17}};

bool cmp(pair<int, int> a, pair<int, int> b)
{
 return a.second < b.second;
}

int main()
{
 vector<pair<int,int>> ans;

 sort(data.begin(), data.end(), cmp);
 ans.push_back(data.front());

 for(int i = 1;i < data.size(); i++)
   if(data[i].first >= ans.back().second)
     ans.push_back(data[i]);

 for(int i = 0; i < ans.size(); i++)
   cout<<"("<<ans[i].first<<", "<<ans[i].second<<"), ";

 cout<<endl;

 return 0;
}

2. 区间选点问题
数轴上有n个闭区间 [ a i , b i ] [a_i, b_i] [ai,bi]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

首先考虑如果两个区间包含,小区间内的点一定在大区间内,大区间内的点不一定在小区间内。所以遇到包含区间,只考虑小区间就可以了。当两个区间不包含但有重叠部分,选择一个点在 b i b_i bi更小的区间的最末端,则该点一定包含在两个区间内。

同样按 b i b_i bi排序所有点,当 b i b_i bi相同时 a i a_i ai从大到小排序。这样排序以后即使有包含区间,小区间也会排在前面。接下来取第一个区间的最后一个点,然后向后查找第一个不包含该点的区间,取该区间的最后一个点,以此类推直到结束。

相同的数据(5,7), (1,2), (4,6), (4,5), (0,3), (3,7), (8,10), (9,15), (16,17)。

#include
#include
#include
using namespace std;
vector<pair<int,int>> data {{5,7}, {1,2}, {4,6}, {4,5}, {0,3}, {3,7}, {8,10}, {9,15}, {16,17}};

bool cmp(pair<int, int> a, pair<int, int> b)
{
 return a.second < b.second || (a.second == b.second && a.first > b.first);
}

int main()
{
 if(data.size() == 0) return -1;
 vector<int> ans;
 sort(data.begin(), data.end(), cmp);
 int current = data.front().second;
 ans.push_back(current);
 for(int i = 1; i < data.size(); i++)
   if(data[i].first > ans.back())
   {
     current = data[i].second;
     ans.push_back(current);
   }

 for(int i = 0; i < ans.size(); i++)
   cout<<ans[i]<<", ";

 cout<<endl;
}

3.区间覆盖问题
数轴上有n个闭区间 [ a i , b i ] [a_i,b_i] [ai,bi],选择尽量少的区间覆盖一条指定线段 [ s , t ] [s,t] [s,t]。当两个区间的起始点都比s小,应当选择两条线段里 b i b_i bi更大的区间,这样同一条线段可以覆盖更长距离。假设选择了线段 3 ( a 3 , b 3 ) 3(a_3,b_3) 3(a3,b3),接下来只需要考虑待覆盖线段的 [ b 3 , t ] [b_3, t] [b3,t]部分,找到 a i < b 3 a_iai<b3 b i b_i bi最大的区间覆盖线段,一直到 [ s , t ] [s,t] [s,t]全部被覆盖。

#include
#include
#include
using namespace std;

vector<pair<int,int>> data {{5,7}, {1,2}, {3,4}, {4,5}, {0,3}, {3,7}, {6,9}, {8,13}, {16,17}};
pair<int, int> line {4,12};

bool cmp(pair<int, int> a, pair<int, int> b)
{
 return a.first < b.first;
}

int main()
{
 vector<pair<int, int>> ans;
 int End = line.first, index;

 sort(data.begin(), data.end(), cmp);

 for(int i = 0; i < data.size(); i++)
 { 
  if(data[i].first <= line.first)
    while(i < data.size() && data[i].first <= line.first)
    {
      if(data[i].second > End)
      {
        End = data[i].second;
        index=i;
      }
     i++;
    }
  else {cout<<"Impossible to cover!"; return -1;}
  ans.push_back(data[index]);
  if(End >= line.second) break;
  line.first = End;
  i--;
 }

 for(int i = 0; i < ans.size(); i++)
   cout<<"("<<ans[i].first<<", "<<ans[i].second<<"), ";

 return 0;
}

你可能感兴趣的:(算法竞赛入门经典)