贪心算法——区间问题——区间选点(最大不相交区间数量)、区间分组、区间覆盖

贪心

选择当前最优情况,通过局部最优解找到全局最优解

区间问题常见思路:
排序:按左端点/右端点/双关键字排序
通常
最大:右
最小:左

区间排序

在这里插入图片描述
在这里插入图片描述
步骤:
1.将每个区间按照右端点从小到大排序
2.从前往后枚举每个区间
如果当前区间已经包含点,换下一个区间
如果当前区间不包含点,尽量选取区间后面的点

#include <iostream>
#include <algorithm>
using namespace std;

const int N=1e5+10;
struct Range{
    int l,r;
    bool operator<(const Range& w)const{
        return r<w.r;
    }
}range[N];
int main(){
    int n;
    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,ed=-1e9;
    for(int i=0;i<n;i++){
        if(ed<range[i].l){
            res++;
            ed=range[i].r;
        }
    }
    printf("%d",res);
    return 0;
}

最大不相交区间数量

在这里插入图片描述
跟上一题选择的区间一样,所以代码也一样

区间分组

在这里插入图片描述
1.将区间按左端点从小到大排序
2.从前往后处理每个区间,判断区间能否放入当前组(看下该组区间是否和当前区间有交集——某一组最后一个区间的最右端点max_r是否大于当前区间的左端点)
如果有交集,则开一个新的组
如果没有交集,则放在该组
小根堆heap存储每个组最右的端点,heap的大小等于组的个数
思想:heap.top()每次给出组中右端点最右的点,如果左端点比最右点还大,一定也不能放入其他组中

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N=1e5+10;
struct Range{
    int l,r;
    bool operator<(const Range&w)const{
        return l<w.l;
    }
}range[N];
int main(){
    int n;
    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);
    
    priority_queue<int,vector<int>,greater<int>>heap;//小根堆
    for(int i=0;i<n;i++){
        auto r=range[i];
        if(heap.empty()||heap.top()>=r.l)heap.push(r.r);
        else{
            heap.pop();
            heap.push(r.r);
        }
    }
    
    
    printf("%d",heap.size());
    return 0;
}

区间覆盖

在这里插入图片描述
1.将所有区间按照左端点大小从小到大排序
2.从前往后依次枚举每个区间,在所有能覆盖start的区间中,选择右端点最大的区间,然后将start更新成右端点的最大值

#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10;
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);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        range[i]={l,r};
    }
    
    
    int ans=0;
    bool success=false;
    
    sort(range,range+n);
    
    for(int i=0;i<n;i++){
       //双指针算法
       int j=i,r=-2e9;
       //遍历所有左端点在start左边,求其右端点最大值
       while(j<n&&range[j].l<=st){
          r=max(r,range[j].r);
          j++;
       }
       
       //最大值都小于start,无解
       if(r<st){
           ans=-1;
           break;
       }//即使满足该条件,但如果r<ed,最终还是没有结果,所以要加上success判断
       
       ans++;//选中该区间,加上
       
       if(r>=ed){
           success=true;
           break;//大于区间有边界,结束
       }
       st=r;
       
       i=j-1;//for循环自动i++,所以先令i-1
    }
    if(!success){
        ans=-1;
    }
    printf("%d",ans);
    
    
    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值