8

贪心算法篇——区间问题 - 秋落雨微凉

 2 years ago
source link: https://www.cnblogs.com/qiuluoyuweiliang/p/16926856.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

贪心算法篇——区间问题

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

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

/*题目名称*/

区间选点

/*题目介绍*/

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

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

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

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

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

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

/*数据范围*/
    
1 ≤ N ≤ 105,
−109 ≤ ai ≤ bi ≤ 109

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

/*输出样例*/
    
2

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

/*贪心思想*/

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

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

我们给出实际代码展示:

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;
    }
}

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

/*题目名称*/

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

输出最小组数。

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

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

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

/*数据范围*/
    
1 ≤ N ≤ 105,
−109 ≤ ai ≤ bi ≤ 109

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

/*输出样例*/
    
2

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

/*贪心思想*/

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

我们给出具体实现代码:

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);
    }
}

我们先来介绍一下题目:

/*题目名称*/

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

输出最少区间数,如果无法完全覆盖则输出 −1。

/*输入格式*/
    
第一行包含两个整数 s 和 t,表示给定线段区间的两个端点。

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

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

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

如果无解,则输出 −1。

/*数据范围*/
    
1 ≤ N ≤ 105,
−109 ≤ ai ≤ bi ≤ 109,
−109 ≤ s ≤ t ≤ 109

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

/*输出样例*/
    
2

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

/*贪心思想*/

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

我们给出实际代码展示:

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);
    }
}

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


Recommend

  • 18
    • labuladong.github.io 4 years ago
    • Cache

    贪心算法之区间调度问题

    贪心算法之区间调度问题 👆让天下没有难刷的算法!若 GitBook 访问太慢,可尝试

  • 9

    CSS进阶内容—浮动和定位详解 我们在学习了CSS的基本知识和盒子之后,就该了解一下网页的整体构成了 当然如果没有学习之前的知识,可以到我的主页中查看之前的文章:

  • 11

    JavaScript进阶内容——DOM详解 当我们已经熟练掌握JavaScript的语法之后,我们就该进入更深层次的学习了 首先我们思考一下:JavaScript是用来做什么的? JavaScript诞生就是为了能够让它在浏览器中运行

  • 4

    基本算法篇——二分查找 本次我们介绍基础算法中的二分查找,我们会从下面几个角度来介绍二分查找: 二分查找简述 二分查找模板 二分查找边界 例题数的范围

  • 8

    JVM学习笔记——类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令

  • 9

    JUC学习笔记——共享模型之管程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的管程部分 我们会分为以下几部分进行介绍: 共享问题解决方案 线程安全分析 Monitor

  • 7

    JUC学习笔记——共享模型之内存 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分 我们会分为以下几部分进行介绍: Java内存模型 模式之两阶段终止 模式之Balk...

  • 11

    数据结构篇——KMP算法 本次我们介绍数据结构中的KMP算法,我们会从下面几个角度来介绍: Next示例 Next代码 首先我们先介绍适用于KMP算法的问题: 给定一个字符串S,以及一...

  • 8

    数据结构篇——哈希表 本次我们介绍数据结构中的哈希表,我们会从下面几个角度来介绍: 哈希表介绍 例题模拟散列表的两种方法 字符串前缀哈希法 哈希表介绍 首...

  • 3

    贪心算法篇——经典题型 本次我们介绍贪心算法篇的经典题型,我们会从下面几个角度来介绍: Huffman树 排序不等式 绝对值不等式 Huffman树 我们直接给出对应...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK