贪心——区间问题

贪心就感觉是一个目光很短的人看问题,每次都在一个有限的区域内找到最优解,然后再往后看,贪心也没有具体的套路,所以就把看课程的几道题总结一下。

区间选点
  • 问题描述

给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。

  • 分析

首先将每个区间按右端点从小到大排序,然后遍历所有区间,每次都很贪心的取区间的右端点,如果下一个区间已经覆盖了前一个区间的右端点,就跳过。
算法的正确性可以通过模拟示例进行感受,贪心好像严格证明很难证明,另外我又那么菜,只能从实验的角度感受算法的正确性。

  • 代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int  N = 1e5+10;
vector<PII> q;
int n;

int main()
{
    scanf("%d",&n);
    while(n--){
        int l,r;
        scanf("%d%d",&l,&r);
        q.push_back({r,l});
    }
    sort(q.begin(),q.end());
    int res = 1;
    int t = q[0].first;
    for(int i=1;i<q.size();i++){
        int a = q[i].first,b = q[i].second;
        if(t<=a&&t>=b)continue;
        else{
            t = a;
            res++;
        }
    }
    printf("%d",res);
    return 0;
}
最大不想交区间数量

和上题方法一样。

区间分组
  • 描述

给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。

  • 分析

将区间按左端点从小到大排序,然后遍历所有区间,将可能的分组加到一个小根堆中,每次遍历时如果区间的左端点小于等于堆中最小的右端点,则一定会不能加入现有的分组,需要直接加入堆中;反之可以加入现有的分组,同时右端点的最小值应该更新。

  • 代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
vector<PII> q;
int n;

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        q.push_back({a,b});
    }
    sort(q.begin(),q.end());
    priority_queue<int,vector<int>,greater<int>>heap;
    for(int i=0;i<n;i++){
        auto t = q[i];
        if(heap.empty()||heap.top()>=t.first)heap.push(t.second);
        else{
            heap.pop();
            heap.push(t.second);
        }
    }
    printf("%d",heap.size());
    return 0;
}
区间覆盖
  • 描述

给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出 −1。

  • 分析

先将区间按左端点排序,然后遍历所有区间,每次取所有能覆盖线段左起始点的区间的最大的右端点,并用该右端点更新线段的起始点。

  • 代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
vector<PII>q;
int s,t,n;

int main()
{
    scanf("%d%d%d",&s,&t,&n);
    for(int i=0;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        q.push_back({a,b});
    }
    sort(q.begin(),q.end());
    int res = 0;
    for(int i=0;i<n;i++){
        int j = i,st = -2e9;
        while(j<n&&q[j].first<=s){
            st = max(st,q[j].second);
            j++;
        }
        res++;
        s = st;
        if(s>=t)break;
    }
    if(s<t)printf("-1");
    else printf("%d",res);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

up-to-star

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值