1、将每个区间按照右端点从小到大进行排序
2、从前往后枚举区间,end值初始化为无穷小
如果本次区间不能覆盖掉上次区间的右端点, ed < range[i].l
说明需要选择一个新的点, res ++ ; ed = range[i].r;
如果本次区间可以覆盖掉上次区间的右端点,则进行下一轮循环
#include
#include
using namespace std;
const int N = 100010;
int n;
struct Range//区间范围
{
int l, r;//左右端点
bool operator< (const Range &W)const//重载<,用右端点排序,从小到大
{
return r < W.r;
}
}range[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].r);
sort(range, range + n);//升序,从小到大
int res = 0, ed = -2e9;//res是当前选择的点的数量,ed是上一个点(初始化为负无穷)
for (int i = 0; i < n; i ++ )//枚举所有区间
if (ed < range[i].l)//如果上一个点小于这个区间的左端点
{
res ++ ;//点的数量+1
ed = range[i].r;//选择新的点
}
printf("%d\n", res);
return 0;
}
代码与上题完全一样:
上题求的是最少要用几个点让每个区间都有点,即不相交的区间个数。
#include
#include
using namespace std;
const int N = 100010;
int n;
struct Range//区间范围
{
int l, r;//左右端点
bool operator< (const Range &W)const//重载<,用右端点排序,从小到大
{
return r < W.r;
}
}range[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].r);
sort(range, range + n);//升序,从小到大
int res = 0, ed = -2e9;//res是当前选择的点的数量,ed是上一个点(初始化为负无穷)
for (int i = 0; i < n; i ++ )//枚举所有区间
if (ed < range[i].l)//如果上一个点小于这个区间的左端点
{
res ++ ;//点的数量+1
ed = range[i].r;//选择新的点
}
printf("%d\n", res);
return 0;
}
贪心决策
从前往后枚举每个区间,判断此区间能否将其放到现有的组中
如果一个区间的左端点比最小组的右端点要小,ranges[i].l<=heap.top() , 就开一个新组 heap.push(range[i].r);
如果一个区间的左端点比最小组的右端点要大,则放在该组, heap.pop(), heap.push(range[i].r);
每组去除右端点最小的区间,只保留一个右端点较大的区间,这样heap有多少区间,就有多少组。
#include
#include
#include
using namespace std;
const int N=100010;
int n;
struct Range
{
int l,r;
bool operator <(const Range &W)const//重载<,用左端点从小到大排序
{
return l,greater>heap;//小根堆,自动把最小值放到根节点,这里放入的是右端点
for (int i = 0; i < n; i ++ )
{
//如果堆为空,或者堆的右端点大于i的左端点,则需要开一个新空间给i区(因为区间不能重合)
if (heap.empty() || heap.top() >= range[i].l)
{
heap.push(range[i].r);
}
//如果可以放进来(即i的左端点大于该堆的右端点),则更新堆的右端点(不要上次更新的右端点,用这次的,下面注释有解释)
else
{
heap.pop();
heap.push(range[i].r);
}
//例:[1,2],[3,4],这两个可以合并=[1,4],top=4
// [2,3],则不能合并=[2,3][1,4](小跟堆按右端点排序),top=3
// [4,5],不用看右端点更大的[1,4],只用看跟的右端点,因为跟的右端点是最小的,不可能小于跟的右端点反而能大于子的右端点
//因为range也从小到大排序了,所以不用担心i比i+1大,导致根可以合并i+1再合并i却因为合并了i而变大无法合并i+1
//所以只看[2,3]即可合并=[1,4][2,5],top=4.下次又是用[1,4]来看了
}
printf("%d\n", heap.size());
return 0;
}
#include
#include
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l, r;
bool operator< (const Range &W)const//左端点从小到大排序
{
return l < W.l;
}
}range[N];
int main()
{
int st, ed;//线段区间的左右端点
scanf("%d%d", &st, &ed);
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);//左端点从小到大排序
int res = 0;
bool success = false;
for (int i = 0; i < n; i ++ )
{
int j = i, r = -2e9;//双指针遍历,j是第几个区间,r是新区间的右端点
while (j < n && range[j].l <= st)//遍历所有区间的左端点在start的左边,并且右端点最大的
{
r = max(r, range[j].r);
j ++ ;
}
if (r < st)//如果没有右端点在start右边的,说明无解,直接出去
{
res = -1;
break;
}
res ++ ;//选择区间+1
if (r >= ed)//如果当前的右端点大于end,说明已经全覆盖了线段区间了,直接出去
{
success = true;
break;
}
st = r;//如果选了区间,并且当前还未全覆盖,则让start为新的右端点,在此后面找区间
i = j - 1; //因为已经选了新区间j了,下次从j+1开始找。又因为上面j++最后多加了一次,而且i会++,所以i=j-1
}
if (!success) res = -1;
printf("%d\n", res);
return 0;
}