前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >区间选点

区间选点

作者头像
秋落雨微凉
发布于 2022-12-02 08:37:10
发布于 2022-12-02 08:37:10
91100
代码可运行
举报
运行总次数:0
代码可运行

贪心算法篇——区间问题

本次我们介绍贪心算法篇的区间问题,我们会从下面几个角度来介绍:

  • 区间选点
  • 区间分组
  • 区间覆盖

区间选点

我们首先来介绍第一道题目:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*题目名称*/

区间选点

/*题目介绍*/

给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。

位于区间端点上的点也算作区间内。

/*输入格式*/
    
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

/*输出格式*/
    
输出一个整数,表示所需的点的最小数量。

/*数据范围*/
    
1N105,109 ≤ ai ≤ bi ≤ 109

/*输入样例*/
    
3
-1 1
2 4
3 5

/*输出样例*/
    
2

我们对题目采用贪心算法来思考:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*贪心思想*/

我们所使用的每一步都是目前该问题的最优解!
    
/*问题分析*/
    
我们需要在n个区间里设置m个点,使每个区间中至少有一个点

那么我们的每个点的取值必须是概括一个点,且最有可能概括多个点
    
那么我们可以对区间进行排序:我们根据区间的右端点进行排序,然后如果该区间没有对应的点,我们就将该区间的右端点设置为其中的点
    
由于我们该区间左侧没有不符合条件的点,所以不用顾及左侧,而右侧可能存在其他区间也概括这个点,我们可以进行判断,若含该点,跳过即可

我们给出实际代码展示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.*;

public class Main{
    
    static int N = 100010,INF = 0x3f3f3f3f,n;
    
    // 结构体创建数组需要定义成全局变量
    static Range[] range = new Range[N];
    
    public static void main(String[] args){
        
        Scanner scan = new Scanner(System.in);

        // 初始值输入
        n = scan.nextInt();
        for(int i = 0 ; i < n ; i ++ ){
            int l = scan.nextInt();
            int r = scan.nextInt();
            range[i] = new Range(l,r);
        }
        
        //结构体排序
        Arrays.sort(range,0,n); 

        // 表示一共需要多少点
        int res = 0;
        
        // 上一个点的右端点(最开始为负无穷,为了使第一个区间必定赋值)
        int ed = -INF; 
        
        // 开始遍历所有区间并挨个判断
        for(int i = 0 ; i < n ; i ++ ){
            if(range[i].l > ed){
                res ++ ;
                ed = range[i].r;
            }
        }
        
        // 最后输出结果即可
        System.out.println(res);
    }
}

// 区间class,因为我们需要重新设置排序条件,所以使用一个类,重塑compareTo方法
class Range implements Comparable<Range>{
    
    // l左端点,r右端点
    int l,r;
    
    // 构造方法
    public Range(int l,int r){
        this.l = l;
        this.r = r;
    }
    
    // 排序比较
    public int compareTo(Range o){
        return this.r - o.r;
    }
}

区间分组

我们首先来介绍一下题目:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*题目名称*/

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

输出最小组数。

/*输入格式*/
    
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

/*输出格式*/
    
输出一个整数,表示最小组数。

/*数据范围*/
    
1N105,109 ≤ ai ≤ bi ≤ 109

/*输入样例*/
    
3
-1 1
2 4
3 5

/*输出样例*/
    
2

我们采用贪心思想来分析一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*贪心思想*/

我们所使用的每一步都是目前该问题的最优解!
    
/*问题分析*/
 
该题目要求将n个区间划分为m个组,使组中的区间不能接壤
    
该题和第一题不同之处在于:第一题在排序之后每个区间和后面的区间有关联,不会越界;但该题后面的区间仍旧可以放在前面的组中使用
    
我们同样采用最优解思考,我们依旧将区间排序:我们首先将区间按照左端点进行从小到大排序
    
我们从头开始遍历区间并做判断:
    1.将该区间的左端点与之前每个组的右端点进行判断(我们用p表示区间,用s表示组)
    2.若p[i].l > s[j].r:说明两者不接壤,可以将该点放到该组中
    3.若所有组都不符合上述条件,就重新创建一个组即可

我们给出具体实现代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.*;

public class Main{
    
    static int N = 100010,n;
    
    // 存放区间
    static Range[] range = new Range[N];
    
    public static void main(String[] args){
        
        Scanner scan = new Scanner(System.in);
        
        // 初始化
        n = scan.nextInt();
        for(int i = 0 ; i < n ; i ++ ){
            int l = scan.nextInt();
            int r = scan.nextInt();
            range[i] = new Range(l,r); 
        }

        // 排序
        Arrays.sort(range,0,n);

        // 我们采用PriorityQueue让其按从小到大的顺序排列,方便我们后面遍历从小到大遍历
        Queue<Integer> minheap = new PriorityQueue<>(); 
        
        // 开始遍历
        for(int i = 0 ; i < n ; i ++ ){
            
            Range r = range[i];
            
            // 小根堆的最小值要大于等于。因为相等也是有交点
            if(minheap.isEmpty() || minheap.peek() >= r.l){
                // 若不满足条件,自己创建一个组
                minheap.add(r.r);
            }else{
                // 若满足条件,将该组抛出,重新加入一个组(因为无法更改数据,我们采用这种形式表示更换组的右端点数据)
                minheap.poll();
                minheap.add(r.r);
            }
        }
        
        // 输出结果
        System.out.println(minheap.size());
    }
}

// 区间Class
class Range implements Comparable<Range>{
    int l,r;
    public Range(int l,int r){
        this.l = l;
        this.r = r;
    }
    public int compareTo(Range o){
        return Integer.compare(l,o.l);
    }
}

区间覆盖

我们先来介绍一下题目:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*题目名称*/

区间覆盖
    
/*题目介绍*/
    
给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。

输出最少区间数,如果无法完全覆盖则输出 −1/*输入格式*/
    
第一行包含两个整数 s 和 t,表示给定线段区间的两个端点。

第二行包含整数 N,表示给定区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

/*输出格式*/
    
输出一个整数,表示所需最少区间数。

如果无解,则输出 −1/*数据范围*/
    
1N105,109 ≤ ai ≤ bi ≤ 109,109 ≤ s ≤ t ≤ 109

/*输入样例*/
    
1 5
3
-1 3
2 4
3 5

/*输出样例*/
    
2

我们采用贪心的思想进行分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*贪心思想*/

我们所使用的每一步都是目前该问题的最优解!
    
/*题目分析*/
    
我们希望用n个区间去覆盖一块[s,t]之间的区间
    
那么我们每次使用的一个区间,自然是希望该区间所覆盖的目的部分越大越好,而且我们依旧覆盖过的区间可以直接抛出
    
那么我们只需要找到每次满足覆盖条件的区间组,并在组中找到一个最优解即可
    
我们将n个区间进行以左端点从小到大排序的操作
    
在排序结束之后,我们从头开始遍历,我们设st为目的起点,ed为目的终点
    
我们开始判断,我们需要该区间的左端点小于等于st,且区间的右端点尽可能的大
    
那么我们可以设置条件:p[i].l <= st 这时进入选择区域
    
然后我们需要选择一个右端点最大的区间,我们可以全部选择,用max来判定即可:maxr = Math.max(maxr,p[i].r)
    
当最后该组内的选择结束后,我们首先需要判断是否符合条件(是否可以覆盖起始点),然后我们再去更新起始点的位置进行下一轮判定

我们给出实际代码展示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.*;

public class Main{
    
    static int N = 100010;
    
    static Range[] range = new Range[N];
    
    public static void main(String[] args){
        
        Scanner scan = new Scanner(System.in);
        
        int st = scan.nextInt();
        int ed = scan.nextInt();
        int n = scan.nextInt();
        
        for(int i = 0 ; i < n ; i ++ ){
            int l = scan.nextInt();
            int r = scan.nextInt();
            range[i] = new Range(l,r);
        }
        
        Arrays.sort(range,0,n);

        // 表示返回值,也就是最少多个区间可以覆盖
        int res = 0;
        
        // 表示是否成功
        boolean success = false;
        
        // 使用双指针算法,来查找每个 小于等于 st的右端点最长的数
        for(int i = 0 ; i < n ; i ++ ){ 
            
            // 这里j就是另一个指针,让j移动判断,最后更新i
            int j = i;
            
            // 我们第一个maxr需要负无穷以便于可以更新
            int maxr = (int)-(2e9);
            
            // 将所有左端点小于st的数的右端点进行比较,取出最大值
            while(j < n && range[j].l <= st){ 
                maxr = Math.max(maxr,range[j].r);
                j ++ ;
            }

            // 如果右端点最大的点小于st,就说明覆盖失败,success为false(默认)
            if(end < st)  break; 

            // 每进行一次就相当于加入了一个区间,我们的最小区间值需要++
            res ++; 

            // 如果进行到这一步完全覆盖了,就标记一下,然后break
            if(end >= ed){ 
                success = true;
                break;
            }
            
            // 每选取一个区间,就将st赋值成这个区间的右端;
            st = maxr;
            
            // 然后更新i,将i更新到j的前一位,也就是大于之前的st的第一位,然后继续判断
            i = j - 1; 
        }
        
        // 如果没有标记就是说明没有完全覆盖,将结果复制成-1
        if(!success) res = -1; 
        
        // 最后输出res
        System.out.println(res);
    }
}

// Class类表示区间,更新了compareTo方法
class Range implements Comparable<Range>{
    int l,r;
    public Range(int l,int r){
        this.l = l;
        this.r = r;
    }
    public int compareTo(Range o){
        return Integer.compare(l,o.l);
    }
}

结束语

好的,关于贪心算法篇的区间问题就介绍到这里,希望能为你带来帮助~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
贪心算法篇——经典题型
贪心算法篇——经典题型 本次我们介绍贪心算法篇的经典题型,我们会从下面几个角度来介绍: Huffman树 排序不等式 绝对值不等式 推公式 Huffman树 我们直接给出对应题型: /*题目名称*/ 合并果子 /*题目介绍*/ 在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。 达达决定把所有的果子合成一堆。 每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。 可以看出,所有的果子经过 n−1 次合并之后,就只剩下一堆了。
秋落雨微凉
2022/12/02
3760
【简单】区间和并
给定 n 个区间 \left[ {{{\rm{l}}_i},{r_i}} \right],要求合并所有有交集的区间。注意:如果在端点处相交,也算有交集。输出合并完成后的区间个数。例如:\left[ {1,3} \right],\left[ {2,6} \right] 可以合并为一个区间 \left[ {1,6} \right]。
字节星球Henry
2021/08/09
5430
贪心算法——区间选点与最大不相交区间数
给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
用户10604450
2024/03/15
1680
贪心算法——区间选点与最大不相交区间数
《算法竞赛进阶指南》0x07 贪心
贪心类问题无疑是基础算法中难度最大的,难点在于思维的跳跃性,没有固定的解题模式,往往是一类题一种解法或结论
一只野生彩色铅笔
2022/10/31
8350
区间合并(c++,java)
给定一个长度为 n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
GeekLiHua
2025/01/21
290
区间合并算法及模板应用
将多个区间进行合并,其中有交集的区间合为一个区间,没有交集的区间保留原状。注意,这里端点重合也算作一种交集区间。
timerring
2023/05/24
6300
区间合并算法及模板应用
算法基础:区间合并算法及模板应用
将多个区间进行合并,其中有交集的区间合为一个区间,没有交集的区间保留原状。注意,这里端点重合也算作一种交集区间。
timerring
2022/11/28
8760
算法基础:区间合并算法及模板应用
区间合并讲解
用户10604450
2024/03/15
950
【算法】双指针、位运算、离散化、合并区间
双指针的算法可以优化时间复杂度,双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向( 快慢指针 )或者相反方向( 对撞指针 )的指针进行扫描,从而达到相应的目的。将双层暴力循环O(n^2)优化到O(n),所以双指针算法最核心的用途就是优化时间复杂度。双指针是比较常见的把,直接看例子既可以。
平凡的人1
2023/10/15
2030
【算法】双指针、位运算、离散化、合并区间
【算法/训练】:贪心(算法 & 题目训练)
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
IsLand1314
2024/10/22
1260
【算法/训练】:贪心(算法 & 题目训练)
区间合并
给定 n 个区间 [li,ri],要求合并所有有交集的区间。 注意如果在端点处相交,也算有交集。 输出合并完成后的区间个数。 例如:[1,3]和[2,6]可以合并为一个区间[1,6]。
dejavu1zz
2020/10/23
6820
莫队算法
 莫队算法是由莫涛提出的算法,可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。
mathor
2018/09/19
6740
基础算法——区间合并
由于有些读者朋友私聊我,希望出几期基础算法的讲解,kmp,dp,哈希,搜索,贪心等对初学者还是不太友好,所以我打算更新几期基础算法合集,没办法谁让我宠粉丝呢?彦祖,热巴说你呢,快关注!
秋名山码神
2022/12/13
2200
基础算法——区间合并
Cleaning Shifts POJ - 2376 (经典区间贪心)
Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning chores around the barn. He always wants to have one cow working on cleaning things up and has divided the day into T shifts (1 <= T <= 1,000,000), the first being shift 1 and the last being shift T. Each cow is only available at some interval of times during the day for work on cleaning. Any cow that is selected for cleaning duty will work for the entirety of her interval. Your job is to help Farmer John assign some cows to shifts so that (i) every shift has at least one cow assigned to it, and (ii) as few cows as possible are involved in cleaning. If it is not possible to assign a cow to each shift, print -1.
ACM算法日常
2019/08/01
8310
Cleaning Shifts POJ - 2376 (经典区间贪心)
贪心算法思想与练习
给定一个长度为 N 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。
timerring
2023/03/24
6180
贪心算法思想与练习
重新排序-研究生组G题
  给定一个数组 A 和一些查询 Li,Ri, 求数组中第 Li 至第Ri个元素之和。
别团等shy哥发育
2023/03/23
1.2K0
重新排序-研究生组G题
ST表和区间最值
ST表可以通过 O(nlogn) 的预处理然后在 O(1) 的时间内算出某段区间的最值,空间复杂度也为 O(nlogn)。原理是利用了倍增和动态规划的思想,设 dp[i][j] 表示从第 i 个数开始的 2^j 个数的最值,状态转移为:dp[i][j] = max(dp[i][j-1],dp[i + (2^{j-1})][j-1]),若求最小值则用 min ,即将长度为 2^j 的区间对半分为两个长度为 2^{j-1} 的两个小区间,分别求最值 。由于要用到log运算,介绍一种 log_2 的预处理方法:
Here_SDUT
2022/08/11
8170
基础算法篇——区间合并
基础算法篇——区间合并 本次我们介绍基础算法中的区间合并,我们会从下面几个角度来介绍: 区间合并 区间合并 我们这次的目的很简单: 快速高效的完成区间合并任务 区间合并的要求: 提供若干个区间,将有接壤的部分变为一个区间,没有接壤的部分不改变 例如[1,2],[2,3],[4,5],[6,7],[6,8]五个区间,我们需要将他们变为三个区间[1,3],[4,5],[6,8] 我们给出主要思想: /* 1.首先我们以每个分区的左侧为标准进行排序,这样我们的每次区间合并只需要采用当前区间和下一个区间对比即可,此
秋落雨微凉
2022/11/16
3890
网易校招真题一
为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准来帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。 输入描述: 每个输入包含一个测试用例。
Tim在路上
2020/08/04
4880
【蓝桥杯2022省赛】蓝桥杯题目笔记 Java版本 数位排序、求阶乘基础与灵活分析
1 到 13 的排序为: 1,10,2,11,3,12,4,13,5,6,7,8,91,10,2,11,3,12,4,13,5,6,7,8,9 。第 5 个数为 3 。
小小程序员
2023/02/05
7290
相关推荐
贪心算法篇——经典题型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文