#M8023. 贪心算法:3个经典区间问题
贪心算法:3个经典区间问题
经典区间问题的贪心解法
最大不相交区间数量
leetcode 435 无重叠的子区间
给定一个区间的集合 A ,其中 A[i] = [starti,endi]。返回需要移除区间的最小数量,使剩余区间互不重叠。
//题目的完整代码
struct Intervals{
int st, ed;
} a[100005];
bool cmp(Intervals x, Intervals y) {
return x.ed < y.ed;
}
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int n = intervals.size();
for (int i = 0; i < n; i++) {
a[i].st = intervals[i][0];
a[i].ed = intervals[i][1];
}
sort(a, a + n, cmp);
int sum = 1, st = a[0].st, ed = a[0].ed;
for (int i = 1; i < n; i++) {
if (a[i].st >= ed) {
sum++;
st = a[i].st;
ed = a[i].ed;
}
}
return n - sum;
}
};
区间分组
给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。 输出最小组数。
我们可以把所有开始时间和结束时间排序,遇到开始时间就把需要的教室加 1,遇到结束时间就把需要的教室减 1,在一系列需要的教室个数变化的过程中,峰值就是多同时进行的活动数,也是我们至少需要的教室数。
//题目的完整代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
int b[2*N], idx;
int main(){
scanf ("%d", &n);
for (int i = 0; i < n; i ++) {
int l, r;
scanf("%d %d", &l, &r);
b[idx ++] = l * 2;//标记左端点为偶数。
b[idx ++] = r * 2 + 1;// 标记右端点为奇数。
}
sort(b, b + idx);
int res = 1, t = 0;
for (int i = 0; i < idx; i ++){
if(b[i] % 2 == 0) t ++;
else t --;
res = max(res, t);
}
printf ("%d\n", res);
return 0;
}
最小区间覆盖
给定 N 个闭区间 [ai,bi]以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出 −1。
分析: 第一步,将所有区间按照左端点从小到大进行排序 第二步,从前往后枚举每个区间,在所有能覆盖start的区间中,选择右端点的最大区间,然后将start更新成右端点的最大值
//题目的完整代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
struct Range{
int l, r;
bool operator< (const Range &W)const
{
return l < W.l;
}
}range[N];
int main(){
int st, ed;
scanf("%d%d", &st, &ed);
scanf("%d", &n);
for (int i = 0; i < n; i ++ ){
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
int res = 0;
bool success = false;
for (int i = 0; i < n; i ++ ){
int j = i, r = -2e9;
while (j < n && range[j].l <= st){
r = max(r, range[j].r);
j ++ ;
}
if (r < st){
res = -1;
break;
}
res ++ ;
if (r >= ed){
success = true;
break;
}
st = r;
i = j - 1;
}
if (!success) res = -1;
printf("%d\n", res);
return 0;
}
找拐点
求一个最长序列,使得该序列的任意三个相邻元素,中间的元素是三个中最大的,或者是最小的(最长波浪序列)
//题目的完整代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> h(n);
for (int i = 0; i < n; i++) cin >> h[i];
int x = 0;
while (x+1 < n and h[x+1] == h[x]) ++x; // 开始如果一段相同的则去重,取哪盆都一样
bool f = false;
if (h[x+1] > h[x]) f = true; // 确定一开始是上升还是下降的
int ans = 1;
for (int i = x+1; i < n; ++i) {
if (i == n-1) ans++; // 结尾没有拐点直接加
else if (f && h[i+1] < h[i]) { ans++; f = 0; } // 找到波峰
else if (!f && h[i+1] > h[i]) { ans++; f = 1; } // 找到波谷
}
cout << ans << '\n';
return 0;
}