贪心 - 区间问题 - 区间覆盖

562 阅读1分钟

区间覆盖

  • 问题背景

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

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

  • 策略

    • 将每个区间按左端点从小到大排序
    • 从前往后枚举每个区间,在所有能覆盖 s 的区间中,选择右端点最大的区间,然后将 s 更新所选区间的右端点

练习

04 区间覆盖

  • 题目

Snipaste_2023-04-04_19-15-54.png

  • 题解
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 100010;
    public static int n, s, t;
    public static Pair[] range = new Pair[N];

    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));
        String[] str1 = br.readLine().split(" ");
        s = Integer.parseInt(str1[0]);
        t = Integer.parseInt(str1[1]);
        n = Integer.parseInt(br.readLine());
        for (int i = 0; i < n; i++) {
            String[] str2 = br.readLine().split(" ");
            range[i] = new Pair(Integer.parseInt(str2[0]), Integer.parseInt(str2[1]));
        }

        //左端点排序
        Arrays.sort(range, 0, n);
        int res = 0;
        boolean flag = false;
        //遍历所有区间
        for (int i = 0; i < n; i++) {
            //寻找左端点在s左边 且右端点最大的区间
            int j = i;
            //记录右端点最大值
            int r = -(int) 2e9;
            while (j < n && range[j].first <= s) {
                r = Math.max(r, range[j].second);
                j++;
            }

            //如果找到的右端点比s还小 说明这一段区间都无法覆盖s break
            if (r < s) {
                break;
            }

            //这个时候就找到了能覆盖s 且右端点最大的最佳区间 res++
            res++;

            //如果找到的这个区间同时也覆盖了t 那就找到了最后一个区间 flag置为true break
            if (r >= t) {
                flag = true;
                break;
            }

            //更新s为右端点
            s = r;
            //由于s的更新 下一次的寻找就用不上已经遍历过的区间 更新一下
            //这里体现了双指针算法
            i = j - 1;
        }

        if (flag) {
            pw.println(res);
        } else {
            pw.println(-1);
        }

        pw.close();
        br.close();
    }
}