题解:AcWing 803 区间合并

【题目来源】

AcWing:803 区间合并 - AcWing题库

【题目描述】

给定 \(n\) 个区间 \([l_i,r_i]\),要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

输出合并完成后的区间个数。

例如:\([1,3]\)\([2,6]\) 可以合并为一个区间 \([1,6]\)

【输入】

第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含两个整数 \(l\)\(r\)

【输出】

共一行,包含一个整数,表示合并区间完成后的区间个数。

【输入样例】

5
1 2
2 4
5 6
7 8
7 9

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(n\) 个区间 \([l_i, r_i]\),需要将所有有交集(包括端点相交)的区间合并,输出合并后的区间个数。这是一个经典的区间合并问题,关键在于如何高效地识别和合并相交区间。

  2. 算法选择

    • 贪心策略(Greedy):通过排序将相交区间聚集在一起,然后线性扫描合并
    • 按左端点排序:将所有区间按照 \(l_i\) 从小到大排序,这样相交的区间会被排列在相邻位置
    • 维护当前合并区间:使用变量 \(st\)\(ed\) 维护当前正在合并的区间起始和结束位置
  3. 关键步骤

    • 读取输入\(n\)(区间数量)、\(n\) 个区间 \([l_i, r_i]\)
    • 按左端点排序:使用 sort(segs.begin(), segs.end()) 按左端点升序排序
    • 初始化合并区间st = -2e9, ed = -2e9(表示当前无合并区间)
    • 遍历合并(遍历每个区间 \([l, r]\)):
      • 无交集情况ed < l):当前区间与合并区间无交集
        • 如果不是第一个区间,将 \([st, ed]\) 加入结果
        • 开始新区间:st = l, ed = r
      • 有交集情况ed >= l):当前区间与合并区间有交集
        • 合并区间:ed = max(ed, r)
    • 处理最后一个区间:将最后一个合并区间加入结果
    • 输出结果:合并后的区间个数
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),遍历合并 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储区间和合并结果
  5. 区间合并的核心思想

    • 排序预处理:按左端点排序是区间问题的基础,将相交区间聚集在一起
    • 贪心合并策略:维护当前合并区间的最右端点 \(ed\),与新区间的左端点 \(l\) 比较判断是否相交
    • 边界处理:端点相交(ed == l)也算有交集,因此判断条件为 ed < l 而非 ed <= l
    • 扫描线思想:线性扫描已排序的区间,每次处理一个区间,时间复杂度线性
    • 适用于区间重叠检测、区间覆盖、日程安排等问题

【解题思路】

【算法标签】

贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;  // 定义区间类型
const int N = 100010;
int n;  // 区间个数
vector<PII> segs;  // 存储所有区间

// 区间合并函数
void merge(vector<PII> &segs)
{
    vector<PII> res;  // 存储合并后的结果
    sort(segs.begin(), segs.end());  // 按左端点排序

    int st = -2e9, ed = -2e9;  // 当前合并区间的起始和结束
    for (auto seg : segs)  // 遍历所有区间
    {
        if (ed < seg.first)  // 当前区间与合并区间无交集
        {
            if (st != -2e9)  // 如果不是第一个区间
            {
                res.push_back({st, ed});  // 保存前一个合并区间
            }
            st = seg.first, ed = seg.second;  // 开始新的合并区间
        }
        else  // 当前区间与合并区间有交集
        {
            ed = max(ed, seg.second);  // 合并,更新右端点
        }
    }
    if (st != -2e9)  // 处理最后一个区间
    {
        res.push_back({st, ed});
    }
    segs = res;  // 更新原区间
}
int main()
{
    cin >> n;  // 输入区间个数
    for (int i = 0; i < n; i++)  // 输入每个区间
    {
        int l, r;
        cin >> l >> r;
        segs.push_back({l, r});
    }
    merge(segs);  // 合并区间
    cout << segs.size() << endl;  // 输出合并后的区间个数
    return 0;
}

【运行结果】

5
1 2
2 4
5 6
7 8
7 9
3
posted @ 2026-06-14 10:37  团爸讲算法  阅读(5)  评论(0)    收藏  举报