文章目录
0. 前言
玄学的贪心问题,一般全凭直觉。
贪心问题没有固定讨论,没有模板,见多了就好了,证明想法的正确性是很困难的,大多采用反证法。
区间问题无非左端点、右端点、左右端点排序…
1. 区间问题+贪心
贪心思路:
- 区间按左端点从小到大排序
- 从前往后依次枚举每个区间
- 判断能否将其放到某个现有的组中,即:
range[i].l > MAX_r
,区间左端点大于当前组的右端点的最大值则说明不相交,否则必定存在相交部分,因为在此保证了左端点排序- 如果不存在这样的组,则建立一个新组,将其放进去
- 如果存在这样的组,将该区间放入,并更新当前组的右端点的最大值
MAX_r
- 判断能否将其放到某个现有的组中,即:
证明:
- 假设最优解为
ans
个组,以上述贪心思路选出来的组为cnt
个。即证明ans = cnt
,等价于ans >= cnt && ans <= cnt
- 首先,以上述贪心思路选择出的
cnt
个组,组内区间一定两两不交,是一组可行方案。且ans
表示的是所有可行方案的最小值,那么ans <= cnt
成立 - 考虑当有
cnt-1
个组时,枚举的当前即将产生新组的区间i
,意味着这个i
与cnt-1
都有交集,这样才能产生出第cnt
个组。那么针对前面任何一个区间,有交集的含义就是range[i].l <= MAX_r
,且已经枚举的1~i-1
区间是按照左端点进行枚举的。意味着每个组都能找到一个区间,其左端点小于range[i].l
,且右端点大于range[i].l
,则区间i
就和它产生交集。那么区间i
就自成为cnt
组,且1~cnt
组中存在公共点range[i].l
。那么这cnt
个组两两之间是无法合并的,因为存在了公共点,合并后则相交。那么就至少存在cnt
个组,则ans >= cnt
成立 - 要点: 如何判断是否存在一个组的
MAX_r
小于等于当前点的左区间呢?- 小根堆维护所有当前组的
MAX_r
,若有多个组同时满足MAX_r < range[i].l
随意加进去一个组即可。因为更新任意一个组的MAX_r
都是等价的。
- 小根堆维护所有当前组的
- 答案: 输出最后小根堆的元素数量即可,存的都是各个组的
MAX_r
代码:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+5;
int n;
struct Range {
int l, r;
bool operator< (const Range &W) {
return l < W.l;
}
}range[N];
int main() {
cin >> n;
for (int i = 0; i < n; ++i) {
int a, b;
cin >> a >> b;
range[i] = {a, b};
}
sort(range, range + n);
priority_queue<int, vector<int>, greater<int>> heap;
for (int i = 0; i < n; ++i) {
auto r = range[i];
if (heap.empty() || heap.top() >= r.l) heap.push(r.r);
else {
int t = heap.top();
heap.pop();
heap.push(r.r);
}
}
cout << heap.size() << endl;
return 0;
}