由于水平原因,博客大部分内容摘抄于网络,如有错误或者侵权请指出,本人将尽快修改

AcWing 907. 区间覆盖

AcWing 907. 区间覆盖

一、题目核心

  • 给定目标线段区间 [s, t]N 个闭区间,选择最少数量的区间完全覆盖目标线段,无法覆盖则输出 -1
  • 数据范围:1 ≤ N ≤ 10^5,区间端点可取值范围 [-10^9, 10^9]

二、核心解法:贪心算法

核心思路

按左端点排序后,每次在能覆盖当前起点的区间中,选择右端点最大的区间,最大化覆盖范围,从而最小化区间使用数量。

具体步骤

AcWing 907 “区间覆盖”问题(即:用最少的给定区间完全覆盖目标区间 [s, t])在 Java 中的经典实现如下。该解法采用贪心策略 + 双指针,时间复杂度为 O(n log n)(主要来自排序)。


✅ 题目回顾

  • 输入
    • 目标区间 [s, t]
    • N 个候选闭区间 [a_i, b_i]
  • 输出
    • 能完全覆盖 [s, t] 所需的最少区间数
    • 若无法覆盖,输出 -1

✅ Java 实现(标准写法)

import java.util.*;

public class Main {
    static class Interval {
        int l, r;
        Interval(int l, int r) {
            this.l = l;
            this.r = r;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int s = sc.nextInt();
        int t = sc.nextInt();
        int n = sc.nextInt();
        
        Interval[] intervals = new Interval[n];
        for (int i = 0; i < n; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            intervals[i] = new Interval(a, b);
        }

        // 按左端点升序排序
        Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1.l, o2.l));

        int res = 0;          // 使用的区间数量
        int currentEnd = s;   // 当前已覆盖到的位置
        int i = 0;            // 遍历指针

        while (currentEnd < t) {
            int maxRight = currentEnd; // 在能覆盖 currentEnd 的区间中找最远右端点

            // 找所有左端点 <= currentEnd 的区间,并记录最大右端点
            while (i < n && intervals[i].l <= currentEnd) {
                if (intervals[i].r > maxRight) {
                    maxRight = intervals[i].r;
                }
                i++;
            }

            // 如果无法向右扩展,说明覆盖失败
            if (maxRight == currentEnd) {
                System.out.println(-1);
                return;
            }

            // 选择这个最远的区间
            res++;
            currentEnd = maxRight; // 更新已覆盖的右边界
        }

        System.out.println(res);
    }
}

🔍 算法逻辑说明

  1. 排序:按区间左端点 l 升序排列,确保我们从左到右处理。
  2. 贪心扩展
    • 维护当前已覆盖到的位置 currentEnd(初始为 s)。
    • 在所有满足 l <= currentEnd 的区间中,选择 r 最大的那个(即延伸最远)。
  3. 失败判断
    • 如果一轮扫描后 maxRight 没有超过 currentEnd,说明出现“空隙”,无法继续覆盖。
  4. 终止条件:当 currentEnd >= t 时,成功覆盖目标区间。

🧪 示例测试

输入

1 5
3
-1 3
2 4
3 5

执行过程

  • 排序后区间:[-1,3], [2,4], [3,5]
  • 第1轮:currentEnd=1,可选 [-1,3]maxRight=3res=1
  • 第2轮:currentEnd=3,可选 [2,4], [3,5]maxRight=5res=2
  • 5 >= 5,结束,输出 2

✅ 输出:2


⚠️ 注意事项

  • 区间是闭区间,所以 l <= currentEnd 是合法的。
  • 必须处理无法覆盖的情况(如中间有空隙或所有区间都在目标左侧/右侧)。
  • 不要漏掉 i++ 的推进逻辑,否则会死循环。

类似题目:https://leetcode.cn/problems/video-stitching/submissions/679961490/

class Solution {
    class Interval{
        private int l;
        private int r;

        public Interval(int l, int r) {
            this.l = l;
            this.r = r;
        }
    }
     public int videoStitching(int[][] clips, int t) {
        int n = clips.length;
        Interval[] intervals = new Interval[n];
        for (int i = 0; i < n; i++) {
            intervals[i]=new Interval(clips[i][0],clips[i][1]);
        }
        // 按左端点升序排序
        Arrays.sort(intervals, Comparator.comparingInt(o -> o.l));

        int res = 0;          // 使用的区间数量
        int currentEnd = 0;   // 当前已覆盖到的位置
        int i = 0;            // 遍历指针

        while (currentEnd < t) {
            int maxRight = currentEnd; // 在能覆盖 currentEnd 的区间中找最远右端点

            // 找所有左端点 <= currentEnd 的区间,并记录最大右端点
            while (i < n && intervals[i].l <= currentEnd) {
                if (intervals[i].r > maxRight) {
                    maxRight = intervals[i].r;
                }
                i++;
            }

            // 如果无法向右扩展,说明覆盖失败
            if (maxRight == currentEnd) {
                return -1;
            }

            // 选择这个最远的区间
            res++;
            currentEnd = maxRight; // 更新已覆盖的右边界
        }
        return res;
    }
}
posted @ 2025-11-22 23:48  小纸条  阅读(7)  评论(0)    收藏  举报