关于区间贪心的补全

闭包薄雾
• 阅读 2637

之前在博客里总结过贪心算法的相关注意概念,但是由于当时理解不够,并没有很好的总结区间贪心问题,所以在这里做一个总结:

区间贪心算法总的来说有两大题型,一个是区间不相交问题,一个是区间选点问题;
其实第二种问题是第一种问题的子问题,并且对于贪心算法中的概念一定要有所体会;

一、区间不相交

区间不相交问题就是对给定的一些开区间中,尽可能多的选择开区间,使得这些开区间两两没有交集;
对于这个问题,首先要理解重叠区间的概念,也就是对于下列图片所给的情况:

关于区间贪心的补全
当出现这钟情况的时候,我们应该优先选择I1,因为这样的话就可以给其他区间腾出很多的空闲位置;
其次,当消除了所有子区间重叠问题的时候,我们会有如下的情况出现:

关于区间贪心的补全
对于这种情况,我们采用的是对各个区间按照左端点的大小进行排序,此时就会形成上图的情况,每个区间的有右边节点有序;

代码如下所示:

#include<stdlib.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=110;
struct Inteval{
    int x,y;
}I[maxn];
bool cmp(Inteval a,Inteval b){
    if(a.x!=b.x)
        return a.x>b.x;//左端从大到小进行排序
    else
        return a.y<b.y;//有段从小到大进行排序
}
int main(){
    int n;
    while(scanf("%d",&n),n!=0){
        for(int i=0;i<n;i++){
            scanf("%d%d",&I[i].x,&I[i].y);
        }
        sort(I,I+n,cmp);
        int ans=1,lastX=I[0].x;
        for(int i=1;i<n;i++){
            if(I[i].y<=lastX){
                //该情况下为不相交区间
                lastX=I[i].x;
                ans++;
            }
        }
    }
    system("pause");
}

其实最难理解的应该是代码中的处理,这里给出详细的解释:
首先来看排序函数

bool cmp(Inteval a,Inteval b){
    if(a.x!=b.x)
        return a.x>b.x;//左端从大到小进行排序
    else
        return a.y<b.y;//有段从小到大进行排序
}

这里之所以要在左端大小相同的情况下对右端进行递增排序,是为了找出包含区间中的最小的子区间;
这样进行排序的时候,就会变成存在包含关系的区间在一起,但是首位肯定是包含区间中的最小区间;

之后就是主体处理;

while(scanf("%d",&n),n!=0){
        for(int i=0;i<n;i++){
            scanf("%d%d",&I[i].x,&I[i].y);
        }
        sort(I,I+n,cmp);
        int ans=1,lastX=I[0].x;
        for(int i=1;i<n;i++){
            if(I[i].y<=lastX){
                //该情况下为不相交区间
                lastX=I[i].x;
                ans++;
            }
        }
    }

这里注意for循环,其实就是对数组进行上述分析的第二部处理,从右向左,寻找不重合的区间(由于排序函数的操作,找到的必定是重合区间中的最小区间),循环从而使得每次选择出来的都是最小的不重合的区间;

个人认为,区间不重叠的贪心思路主要体现在寻找最小的包含区间上;对于每一块有重合情况的区间,我们只需要找出每一块的重合区间中的最小区间,就可以组合成不重叠的区间,并且这些区间肯定数目最大,符合题意;

二、区间选点
区间选点可以视为第一种问题的衍生问题,目的是将给出开区间(注意,这里是开区间)中选择点,使得每个区间都至少有一个点;
该问题也涉及重叠区间的问题。

关于区间贪心的补全
还是一样,当上述情况发生的时候,我们如果点放在I1内,就会使得I2内也存在点;所以根本上来说,我们还是寻找重叠区间内最小的区间;
因此,我们采用的还是第一类问题的操作,但是需要注意的就是点的选取;
对于有序的序列,我们应该把点选在左端点,而不是右端点;

关于这个问题,可以这样想:

关于区间贪心的补全

对于上述情况,如果选取右端点,就会出现选择两个的情况,但是如果选取左端点,就只用选取一个;
所以,只需要对之前的代码进行修改,修改一个判定条件;
I[i].y<=lastX修改为I[i]<lastX即可

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Kubrnete Kubrnete
4年前
基于活动选择问题的贪心算法
目录问题描述:(问题描述%3A)输入格式(输入格式)输出格式(输出格式)算法描述(算法描述与分析)算法分析(算法分析)算法图示(图解)问题描述:Coda从0时刻开始观看直播,到t时刻结束。一共有n场直播可被选择,已知所有直播场次的起止时间和主播名称,其中第i场直播从ai时刻开始,
Karen110 Karen110
4年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Wesley13 Wesley13
4年前
Java日期时间API系列23
  有时候,往往需要统计某个时间区间的销量等问题,这就需要准确的起始时间,获取准确开始时间00:00:00,获取准确结束时间23:59:59。下面增加了一一些方法,获取当天起始时间,昨天起始时间,当前月第一天开始时间,当前月最后一天结束时间,上个月第一天开始时间,上个月最后一天结束时间,某个指定月的起始结束时间等等。其中月份最后一天往往因为月份不同和
Wesley13 Wesley13
4年前
IP地址定位区间的问题分析
  以前写过一篇Oracle中关于IP地址定位的问题分析,最后引申出了一系列的问题。当时问题紧急严峻,抓取了10053事件定位源头,想出了一个解决妙法,还自鸣得意了下,结果忙活完之后看看行业里的解决方案都大体如此,我的心凉了半截。  我总是希望找到一些与众不同的点来解读这一类问题,结果在偶然的一天从MySQL这里找到了一些思路。   我先来分析
Stella981 Stella981
4年前
AI 科学家带你快速 Get 人工智能最热技术
!(https://pic3.zhimg.com/80/v2af9f6637b50b09be60b00a42f3812d5e_1440w.jpg)日前,京东智联云与贪心学院联合举办的人工智能前沿技
深入理解线段树 | 京东物流技术团队
线段树(SegmentTree)是常用的维护区间信息的数据结构,它可以在O(logn)的时间复杂度下实现单点修改、区间修改、区间查询(区间求和、区间最大值或区间最小值)等操作,常用来解决RMQ问题。RMQ(RangeMinimum/MaximumQuery
菜园前端 菜园前端
2年前
什么是贪心算法?
原文链接:什么是贪心算法?贪心算法是算法设计的一种方法。期盼通过每个阶段的局部最优选择,从而达到全局的最优。但结果不一定是最优的。基础案例场景一零钱兑换现有硬币1元、2元、5元,需要用最少的硬币数量凑够11元。利用贪心算法实现,优先考虑最好的结果就是面值为
深度学习 深度学习
8个月前
牛客网235698题:用滑动窗口寻找最多包含两种字符的最长子串
一、什么是?是一种用于处理/子区间问题的技术。它通过维护一个窗口(通常是子数组或子字符串),在遍历过程中动态调整窗口的边界,从而高效地解决问题。二、算法核心思想1.‌初始化窗口‌:通常从数组/字符串的起始位置开始1.‌扩展窗口‌:移动右边界,扩大窗口范围1