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);
}
}
🔍 算法逻辑说明
- 排序:按区间左端点
l升序排列,确保我们从左到右处理。 - 贪心扩展:
- 维护当前已覆盖到的位置
currentEnd(初始为s)。 - 在所有满足
l <= currentEnd的区间中,选择r最大的那个(即延伸最远)。
- 维护当前已覆盖到的位置
- 失败判断:
- 如果一轮扫描后
maxRight没有超过currentEnd,说明出现“空隙”,无法继续覆盖。
- 如果一轮扫描后
- 终止条件:当
currentEnd >= t时,成功覆盖目标区间。
🧪 示例测试
输入:
1 5
3
-1 3
2 4
3 5
执行过程:
- 排序后区间:
[-1,3], [2,4], [3,5] - 第1轮:
currentEnd=1,可选[-1,3]→maxRight=3→res=1 - 第2轮:
currentEnd=3,可选[2,4], [3,5]→maxRight=5→res=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;
}
}
加油啦!加油鸭,冲鸭!!!

浙公网安备 33010602011771号