贪心:区间问题

本文探讨了贪心算法在解决区间问题中的策略,包括最大不相交区间数量、区间选点和区间覆盖。通过左端点排序,找到每个区间与前一个区间是否有交集,从而确定最优解。在区间分组问题中,使用优先队列来高效地分配区间。这些方法展示了贪心算法在处理区间数据时的有效性和效率。
摘要由CSDN通过智能技术生成

区间问题

  区间问题是贪心问题的一大类,处理区间问题通常可以从排序入手。可以按照左端点排序,右端点排序或者双关键字排序。
区间选点

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n,ans;
struct Range{
    int l,r;
}range[maxn];
bool cmp(Range a,Range b){
    return a.r < b.r;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        range[i] = {a,b};
    }
    sort(range+1,range+1+n,cmp);
    int ed = 0x3f3f3f3f * -1;
    for(int i=1;i<=n;i++){
        if(range[i].l > ed){
            ans ++;
            ed = range[i].r;
        }
    }
    cout<<ans;
    return 0;
}

思路:首先将所有的区间按照右端点从小到大排序,如果当前区间与上一个区间的没有交集。在最终的结果集合,增加选择当前区间的右端点。如果有交集,直接pass.
  利用贪心的思想,每次考虑当前的最优值而当前的最优值又恰好是全局的最优值。
最大不相交区间数量

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n,ans;
struct Range{
    int l,r;
}range[maxn];
bool cmp(Range a,Range b){
    return a.r < b.r;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        range[i] = {a,b};
    }
    sort(range + 1,range + 1 + n,cmp);
    int ed = -1 * 0x3f3f3f3f;
    for(int i = 1;i<=n;i++){
        if(range[i].l > ed){
            ans ++;
            ed = range[i].r;
        }
    }
    cout<<ans;
    return 0;
}

  思路和上一题一样吗,这里给出贪心策略的证明。记答案为ans,贪心策略所选出的解是cnt。为了证明贪心值等于最优值。需要证明ans=cnt。等价地,需要证明ans>=cnt,ans<=cnt。
  根据题干,容易证明ans>=cnt。
  接着证明ans<=cnt.以区间两两无交为例,采用反证法,假设ans>cnt。由于区间是两两无交的,根据贪心策略,每个区间都会包含一个点而ans>cnt可得至少有一个区间中包含了两个点。相当于这个区间被选择了两次,这与题干矛盾,假设不成立。其余情况类似,可证名ans<=cnt。
  综上,ans=cnt.
区间分组

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n;
struct Range{
    int l,r;
}range[maxn];
bool cmp(Range a,Range b){
    return a.l < b.l;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        scanf("%d%d",&range[i].l,&range[i].r);
    }
    sort(range,range+n,cmp);
    priority_queue<int,vector<int>,greater<int> > heap; //heap为小根堆,存储每个组右端点的最大值
    for(int i=0;i<n;i++){
        int l = range[i].l, r = range[i].r;
        if(heap.empty() || heap.top() >= l){  //创建新组
            heap.push(r);
        }else{ //加入到最小组并更新右端点
            heap.pop();
            heap.push(r);
        }
    }
    cout<<heap.size()<<endl; //输出组数
    return 0;
}

  思路:按照区间左端点进行排序,枚举每个区间。如果当前区间左端点大于某个组右端点,将该区间加入到这个组,并更新该组的右端点。否则,如果没有任何组满足,创建新的组。
区间覆盖

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n,st,ed,ans;
struct Range{
    int l,r;
}range[maxn];
bool cmp(Range a,Range b){
    return a.l < b.l;
}
int main(){
    cin>>st>>ed>>n;
    for(int i=0;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        range[i] = {a,b};
    }
    sort(range,range + n,cmp);
    bool flag = false;
    for(int i=0;i<n;i++){ //利用双指针查找每次小于左端点且右端点最大的区间,双指针时间复杂度是O(n).
        int j = i,r = -1 * 0x3f3f3f3f;
        while(j < n && range[j].l <= st){
            r = max(r,range[j].r);
            j++;
        }
        if(r < st){
            ans = -1;
            break;
        }
        ans ++;
        if(r >= ed){ //只有r >= end 才完成了区间覆盖
            flag = true;
            break;
        }
        st = r;
        i = j - 1;
    }
    if(flag) cout<<ans;
    else puts("-1");
    return 0;
}

思路:按照左端点排序,选择小于目标区间左端点且右端点最大的区间。更新目标区间的左端点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值