【贪心】区间问题(区间合并、区间选点、最大不相交区间数量、区间分组、区间覆盖)


区间合并

学习资料:
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)

思路参考:https://leetcode.cn/problems/jump-game/solutions/2798996/liang-chong-li-jie-fang-shi-wei-hu-zui-y-q67s/
image

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即可
image

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;
}

演唱会、计算最多能观看几场演出

image

样例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;
}
posted @   Tshaxz  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
Language: HTML
点击右上角即可分享
微信分享提示