贪心 - 区间问题 - 区间分组

312 阅读2分钟

区间分组

  • 问题背景

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

输出最小组数。

  • 策略

    • 将每个区间按左端点从小到大排序
    • 从前往后枚举每个区间
      • 枚举每个组
        • 如果当前组的右端点小于当前区间的左端点,将其放进去,更新组的右端点为当前区间的右端点
        • 如果每个组的右端点都大于当前区间的左端点,开个新的组,更新组的右端点为当前区间的右端点
  • 优化方案

    • 每次枚举每个组的时候找的是所有组右端点的最小值
    • 因此可以用小根堆来维护每个组的右端点

练习

03 区间分组

  • 题目

Snipaste_2023-04-03_20-25-03.png

  • 题解1
//朴素版
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 100010;
    public static int n;
    public static Pair[] range = new Pair[N];
    //存每个组的右端点
    public static int[] q = new int[N];
    //有多少个组
    public static int res = 0;
    
    public static class Pair implements Comparable<Pair> {
        public int first;
        public int second;

        public Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair o) {
            return this.first - o.first;
        }
    }


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        for (int i = 0; i < n; i++) {
            String[] str1 = br.readLine().split(" ");
            range[i] = new Pair(Integer.parseInt(str1[0]), Integer.parseInt(str1[1]));
        }
        //按照左端点排序
        Arrays.sort(range, 0, n);
        //初始化
        Arrays.fill(q, -(int) 2e9);

        //遍历每一个区间
        for (int i = 0; i < n; i++) {
            //flag表示这个区间是否加入到了一个组中
            boolean flag = false;
            //首先遍历目前所有的组 判断当前区间是否能加入到现有的组中
            for (int j = 0; j < res; j++) {
                //如果当前组的右端点 小于 当前区间的左端点
                if (range[i].first > q[j]) {
                    //将当前区间加入到组中 将组的右端点更新成当前区间的右端点
                    q[j] = range[i].second;
                    flag = true;
                    break;
                }
            }
            //如果当前区间无法加入到现有的组中 新开一个组 将组的右端点更新成当前区间的右端点
            if (!flag) {
                q[res++] = range[i].second;
            }
        }
        pw.println(res);
        pw.close();
        br.close();
    }
}
  • 题解2
//堆优化版
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 100010;
    public static int n;
    public static Pair[] range = new Pair[N];
    //小根堆存储所有组中的右端点
    public static PriorityQueue<Integer> queue = new PriorityQueue<>();

    public static class Pair implements Comparable<Pair> {
        public int first;
        public int second;

        public Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair o) {
            return this.first - o.first;
        }
    }


    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        for (int i = 0; i < n; i++) {
            String[] str1 = br.readLine().split(" ");
            range[i] = new Pair(Integer.parseInt(str1[0]), Integer.parseInt(str1[1]));
        }
        //按照左端点排序
        Arrays.sort(range, 0, n);
        //初始化
        queue.offer(-(int) 2e9);

        for (int i = 0; i < n; i++) {
            //获取堆顶元素 即所有组中右端点的最小值
            int top = queue.peek();
            //如果所有组中右端点的最小值 小于 当前区间的左端点 说明该区间可以加入到已有的组中
            if (top < range[i].first) {
                //更新
                queue.poll();
                queue.offer(range[i].second);
            } else {
                //所有组中右端点的最小值 小于 当前区间的左端点 说明该区间无法加入到已有的组中
                //开一个新的组
                queue.offer(range[i].second);
            }
        }
        //堆的size就是组数
        pw.println(queue.size());
        pw.close();
        br.close();
    }
}