






















给定 ( N ) 个闭区间 ([a_i, b_i]),请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
第一行包含整数 ( N ),表示区间数。
接下来 ( N ) 行,每行包含两个整数 ( a_i, b_i ),表示一个区间的两个端点。
输出一个整数,表示所需的点的最小数量。
( 1 \leq N \leq 10^5 ),( -10^9 \leq a_i \leq b_i \leq 10^9 )
3
-1 1
2 4
3 5
2
贪心问题中,区间问题通常通过排序解决,常见排序方式有:
若没有思路可通过举例尝试找规律。
Q:为啥要按右端点排序呢?
A:选择右端点,是想获取本个线段的最大可达位置,能获得最大的覆盖利益。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n; // 线段数量
int res; // 结果
int ed = -INF; // 当前覆盖区间的结束边界,即右端点位置
// 结构体
struct Node {
int l, r;
// 按每个区间的右端点从小到大排序
const bool operator<(const Node &b) const {
return r < b.r;
}
} range[N];
int main() {
cin >> n;
// 注意这里的数组下标是从0开始的
for (int i = 0; i < n; i++) cin >> range[i].l >> range[i].r;
// 右端点从小到大排序
sort(range, range + n);
// 遍历区间,贪心选点
for (int i = 0; i < n; i++)
if (range[i].l > ed) {
res++;
ed = range[i].r;
}
cout << res << endl;
return 0;
}
要理解证明,需先明确 ( cnt ) 的含义:
假设最优解为 ( ans ),贪心思路选出的点数为 ( cnt ),需证明 ( ans == cnt ),等价于证明 ( ans \geq cnt ) 且 ( ans \leq cnt )。
综上,( ans == cnt ),贪心思路最优。
有一些球形气球贴在 XY 平面的墙面上,气球记录在整数数组 points 中,points[i] = [x_start, x_end] 表示气球水平直径的起始和结束坐标。一支弓箭沿 x 轴垂直射出,在坐标 x 处射出后,所有满足 x_start ≤ x ≤ x_end 的气球都会被引爆。求引爆所有气球所需的最小弓箭数。
points = [[10,16],[2,8],[1,6],[7,12]]2points = [[1,2],[3,4],[5,6],[7,8]]4points = [[1,2],[2,3],[3,4],[4,5]]21 ≤ points.length ≤ 10^5points[i].length == 2-2^31 ≤ x_start < x_end ≤ 2^31 - 1这道题本质是「区间重叠问题」,核心思路与「区间选点」一致——通过排序让重叠区间聚集,再贪心选择最优射击点,最大化单次射击的覆盖范围。
假设区间按右端点排序后为 [1,6]、[2,8]、[7,12]、[10,16]:
class Solution {
public int findMinArrowShots(int[][] intervals) {
if (intervals.length == 0)
return 0;
// 按区间结束时间排序
Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1]));
int count = 1;
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] > end) {
count++;
end = intervals[i][1];
}
}
return count;
}
}
Long.compare(a[1], b[1]) 替代 a[1]-b[1],避免因 x_end 接近 2^31-1 导致的整数溢出。lastShot 用 long 存储,同样是为了防止溢出。O(n log n),核心开销在排序(n 为气球数量),遍历仅需 O(n)。O(log n),排序所需的递归栈空间(Java 内置排序为双轴快排)。[1,2] 和 [2,3],射击点选 2 可同时覆盖,符合题目中「x_start ≤ x ≤ x_end」的条件。x_end 可能达到 2^31-1,直接用 int 相减比较会溢出,必须用包装类或转换为 long 比较。points 为 null 或长度为 0 时,直接返回 0。这道题的核心是利用贪心算法,通过「按右端点排序 + 贪心选射击点」的策略,实现最小弓箭数。关键在于理解「选择右端点作为射击点」能最大化覆盖后续区间,从而减少弓箭数量。该思路同样适用于「区间选点」等类似问题,掌握后可举一反三。
要不要我帮你整理一份同类区间贪心问题汇总,包含题目链接、核心思路和代码模板?
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。