一.区间选点
我们采取这样的策略来选点:step(1)将区间按照右端点的大小从小到大排序;step(2)从前往后依次枚举每个区间,如果当前区间中已经包含点,直接pass,否则选当前区间的右端点。因为右端点是最容易被下一个区间包含进去的,所以我们每次选择的都是当前情况下的局部最优解,这种策略就叫作贪心。
设最优解的点数是ans,按照算法找到的点数是cnt,下面我们证明ans == cnt。首先证明ans<=cnt。根据算法思路,这样选择以后每个区间都包含了点(否则会选右端点),因此算法得到一个可行解。又ans是所有可行解的最小值,因此ans<=cnt。再证明ans>=cnt。如果要执行选一个新的点的操作,那么后一个区间和前一个区间一定无交集。这样一来,我们就至少得到了cnt个互不相交的区间,每个这样的区间我们都选了它的右端点。要覆盖这些区间至少需要cnt个点,因此ans>=cnt。终上,ans == cnt。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int res;
struct range
{
int l,r;
bool operator< (const range W)const
{
return r<W.r;
}
}range[N];
int main()
{
int n;
cin>>n;
//要从0开始读入而不能从1开始读入,因为排序的是编号从0到(n-1)的数据,如果读到n的话会导致最后一个数据没有排序。
//从小到大枚举每个区间
for(int i = 0;i<n;i++)
{
int a,b;
cin>>a>>b;
range[i] = {
a,b};
}
sort(range,range+n);
//ed表示枚举的上一个区间的右端点。初始时赋值为负无穷,这是因为第一次总是要加点的。
int ed = -2e9;
//要从0开始不能从1开始,理由同上
for(int i = 0;i<n;i++)
{
//上一个区间的右端点小于当前区间的左端点,说明两个区间没有交集,那么就要选一个新的点
if(ed<range[i].l)
{
res++;
//更新右端点的值
ed = range[i].r;
}
}
cout<<res<<endl;
}
二.最大不相交区间数量
本题代码与上一题完全相同。下面我们来说明为什么按照上题策略选出的点数即为最大不相交区间数量。设ans为最大不相交区间数量,cnt为按照算法找出的区间数量。根据上题,根据算法选出的区间没有交集,因此是一个可行解。而ans是可行解的最大值,因此ans>=cnt。再证明ans<=cnt。采用反证法。假设ans>cnt,说明我们可以找到ans个互不相交的区间,要用点把这些区间覆盖至少需要ans>cnt个点。但是根据第一题,cnt个点已经可以把所有选出的区间覆盖,矛盾。终上,ans ==cnt。
三.区间分组
对于本题,我们的策略如下:step(1)将所有区间按照左端点从小到大排序;step(2)从前往后枚举所有区间,枚举当前已有的所有组,判断能否将该区间放进已有的某个组中(即判断某个是否有某个组的区间的右端点最大值小于当前区间的左端点,若<,可以放机;否则无法放进)。若可以放进某个组,就放进去并更新该组右端点的最大值;若不存在这样的组,就开一个新组把这个区间放进去。
#include<iostream>
#include<algorithm></