【贪心】区间问题(区间合并、区间选点、最大不相交区间数量、区间分组、区间覆盖)
区间合并
学习资料:
1.A21 排序 区间合并
无哨兵模板,pair
存区间(推荐)
void merge(vector<PII>& segs)
{
sort(segs.begin(), segs.end());
vector<PII> res;
int st = segs[0].first, ed = segs[0].second;
for (auto& [l, r] : segs) //第一个区间不符合ed<l与ed<r故会直接跳过
{
if (ed < l)
{
res.push_back({l, r});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
segs = res;
}
无哨兵写法也可以不用auto
,就是代码量多一点
void merge(vector<PII>& segs)
{
sort(segs.begin(), segs.end());
vector<PII> res;
int st = segs[0].first, ed = segs[0].second;
for (int i = 1; i < segs.size(); i++)
{
int l = segs[i].first, r = segs[i].second;
if (ed < l)
{
res.push_back({l, r});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
segs = res;
}
哨兵写法
void merge(vector<PII> &segs)
{
sort(segs.begin(), segs.end());
vector<PII> res;
int st = -2e9, ed = -2e9;
for (auto &[l, r] : segs)
{
if (ed < l)
{
if (st != -2e9) res.push_back({st, ed});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
无哨兵模板,vector<int>
存区间(Leetcode常见)
vector<vector<int>> res; //答案集合
void solve(vector<vector<int>>& a)
{
sort(a.begin(), a.end());
int st = a[0][0], ed = a[0][1];
for (int i = 1; i < a.size(); i++)
{
int l = a[i][0], r = a[i][1];
if (ed < l)
{
res.push_back({st, ed});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
}
56. 合并区间 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> res;
void solve(vector<vector<int>>& a)
{
sort(a.begin(), a.end());
int st = a[0][0], ed = a[0][1];
for (int i = 1; i < a.size(); i++)
{
int l = a[i][0], r = a[i][1];
if (ed < l)
{
res.push_back({st, ed});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
}
vector<vector<int>> merge(vector<vector<int>>& a) {
solve(a);
return res;
}
};
803. 区间合并
// Problem: 区间合并
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/805/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 110;
int n;
int a[N];
vector<PII> segs;
void merge(vector<PII>& segs)
{
sort(segs.begin(), segs.end());
vector<PII> res;
int st = segs[0].first, ed = segs[0].second;
for (auto& [l, r] : segs) //第一个区间不符合ed<l与ed<r故会直接跳过
{
if (ed < l)
{
res.push_back({l, r});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
segs = res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
while (n--)
{
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}
merge(segs);
cout << segs.size();
return 0;
}
55. 跳跃游戏 - 力扣(LeetCode)
using PII = pair<int, int>;
class Solution {
public:
vector<PII> seg;
void merge(vector<PII>& seg)//区间合并
{
sort(seg.begin(), seg.end());
vector<PII> res;
int st = seg[0].first, ed = seg[0].second;
for (auto& [l, r] : seg)
{
if (ed < l)
{
res.push_back({st, ed});
st = l, ed = r;
}
else if (ed < r) ed = r;
}
res.push_back({st, ed});
seg = res;
}
bool canJump(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n; i++)
{
int l = i, r = i + nums[i];
seg.push_back({l, r});
}
merge(seg);
return seg.size() == 1; //能否合并成一个区间
}
};
区间选点
学习资料:
1.区间选点问题
自己的理解:
将区间按右端点排序,初始化一个右端点ed为负无穷,遍历所有区间,如果当前区间左端点>ed,说明这两个区间没有交集,即必须在当前遍历的区间再选一个点才能覆盖掉当前区间,而这个点我们每次都选右端点来覆盖当前区间,因为这样可以使我们的覆盖范围最大化,这也是贪心的“贪”所在。如果最右端点都无法覆盖当前区间,那就说明已经不得不新选一个点了,所以选点数量+1。遍历完所有的区间,此时的选点数量就是全局最小值。
452. 用最少数量的箭引爆气球 - 力扣(LeetCode)
vector<int>
存区间(Leetcode写法)
class Solution {
public:
int n;
int solve(vector<vector<int>>& seg)
{
sort(seg.begin(), seg.end(), [](vector<int>& a, vector<int>& b) {
return a[1] < b[1];
});
int res = 0;
long long ed = LLONG_MIN;
for (int i = 0; i < n; i++)
{
int l = seg[i][0], r = seg[i][1];
if (ed < l)
{
res++;
ed = r;
}
}
return res;
}
int findMinArrowShots(vector<vector<int>>& points) {
n = points.size();
return solve(points);
}
};
pair
存区间写法(ACM写法)
using PII = pair<int, int>;
using i64 = long long;
class Solution {
public:
vector<PII> seg;
int solve(vector<PII>& seg)
{
sort(seg.begin(), seg.end(), [](PII &a, PII &b) {
return a.second < b.second;
});
int res = 0;
i64 ed = LLONG_MIN;
for (auto &[l, r] : seg)
{
if (ed < l)
{
res++;
ed = r;
}
}
return res;
}
int findMinArrowShots(vector<vector<int>>& points) {
int n = points.size();
for (int i = 0; i < n; i++)
{
int l = points[i][0], r = points[i][1];
seg.push_back({l, r});
}
return solve(seg);
}
};
905. 区间选点
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using PII = pair<int, int>;
vector<PII> seg;
int solve()
{
sort(seg.begin(), seg.end(),
[](PII a, PII b) { return a.second < b.second; });
int res = 0, ed = -2e9;
for (auto &[l, r] : seg)
{
if (ed < l)
{
res++;
ed = r;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n;
cin >> n;
while (n--)
{
int l, r;
cin >> l >> r;
seg.push_back({l, r});
}
cout << solve();
return 0;
}
最大不相交区间数量(与区间选点一模一样)
与区间选点问题思想上可以相互转化,代码上可以互相AC。
最大不相交区间数量与区间选点问题本质上其实是等价的,因为最根本的判断标准一样,都是在遍历区间时,只要右端点能选(先按右端点排序),那就一定选右端点,因为如果选上一个区间的右端点都没有和当前区间重合,那选其他点一定也不会和当前区间重合,这样我就可以保证上一个区间与当前遍历区间不相交,以上一个区间右端点<(若(1,2)(2,3)也视为不相交时就是<=)当前区间左端点作为判断区间不相交的依据,此时答案+1。直到循环结束,得到答案。
435. 无重叠区间 - 力扣(LeetCode)
本题与452. 用最少数量的箭引爆气球 - 力扣(LeetCode)的唯一区别就是端点重合时视为不相交,判断ed<l不相交应该加个等号写成ed<=l即可
class Solution {
public:
int n;
int solve(vector<vector<int>>& seg)
{
sort(seg.begin(), seg.end(), [](vector<int> &a, vector<int> &b) {
return a[1] < b[1];
});
int res = 0, ed = INT_MIN;
for (int i = 0; i < n; i++)
{
int l = seg[i][0], r = seg[i][1];
if (ed <= l)//有等号是因为本题端点相等也算不相交
{
res++;
ed = r;
}
}
return res;
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
n = intervals.size();
return n - solve(intervals);
}
};
908. 最大不相交区间数量
vector数组版本
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 1e5 + 10;
int n;
vector<PII> q;
int solve()
{
sort(q.begin(), q.end(),
[](PII &a, PII &b) { return a.second < b.second; });
int res = 0, ed = -2e9;
for (auto &[l, r] : q)
{
if (ed < l)
{
res++;
ed = r;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
q.push_back({l, r});
}
cout << solve();
return 0;
}
pair数组版本
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 1e5 + 10;
int n;
PII q[N];
int solve()
{
sort(q, q + n, [](PII a, PII b) { return a.second < b.second; });
int res = 0, ed = -2e9;
for (auto &[l, r] : q)
{
if (ed < l)
{
res++;
ed = r;
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
q[i] = {l, r};
}
cout << solve();
return 0;
}
结构体版本
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Node
{
int l, r;
bool operator<(const Node &t) const
{
return r < t.r;
}
} node[N];
void solve()
{
sort(node, node + n);
int res = 0, ed = -2e9;
for (int i = 0; i < n; i++)
{
if (ed < node[i].l)
{
res++;
ed = node[i].r;
}
}
cout << res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
node[i] = {l, r};
}
solve();
return 0;
}
演唱会、计算最多能观看几场演出
样例1
输入
2
720 120
840 120
输出
1
样例2
输入
2
0 60
75 60
输出
2
C++代码
// Problem: #OD268. 演唱会、计算最多能观看几场演出
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 区间问题:求最大不相交区间数量 https://www.acwing.com/problem/content/910/
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1010;
int n;
struct Node
{
int l, r;
bool operator<(const Node &t) const
{
return r < t.r;
}
} node[N];
void solve()
{
sort(node, node + n);
int res = 0, ed = -2e9;
for (int i = 0; i < n; i++)
{
if (ed <= node[i].l)
{//注意本题区间端点可以重合,所以有等号,(0,60)与(60,75)算两个区间
res++;
ed = node[i].r;
}
}
cout << res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
{
int l, len;
cin >> l >> len;
int r = l + len + 15;
node[i] = {l, r};
}
solve();
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程