题解:P1496 火烧赤壁(区间合并算法)

火烧赤壁题解

题目传送门:P1496 火烧赤壁

一、题目描述

给定n个着火区间,每个区间表示为[a,b)(左闭右开),要求计算所有着火区间覆盖的总长度(合并重叠或相邻的区间)。

二、题目分析

我们需要处理多个区间,合并其中重叠或相邻的部分,然后计算合并后各区间的长度之和。关键在于如何高效地合并这些区间。

三、解题思路 + 算法介绍

  1. 区间合并算法
    • 首先将所有区间按照左端点排序
    • 然后遍历排序后的区间,维护当前合并区间的起始点(st)和结束点(ed)
    • 如果当前区间的左端点大于维护区间的右端点,说明不重叠,将维护区间加入结果
    • 否则,更新维护区间的右端点为两者中的较大值
  2. 计算总长度
    • 合并完成后,遍历所有合并后的区间,累加每个区间的长度(b-a)

四、代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
int n;
vector<PII> sect;

// 合并重叠或相邻的区间
void merge(vector<PII> &sect)
{
    vector<PII> res;
    int st = -1e10, ed = -1e10; // 初始化为极小值
    sort(sect.begin(), sect.end()); // 按左端点排序
    
    for (auto seg : sect)
    {
        if (ed < seg.first) // 当前区间与维护区间无重叠
        {
            if (st != -1e10) // 不是初始状态,将维护区间加入结果
            {
                res.push_back({st, ed});
            }
            st = seg.first, ed = seg.second; // 开始维护新区间
        }
        else // 有重叠,合并区间
            ed = max(ed, seg.second);
    }
    
    // 加入最后一个维护的区间
    if (st != -1e10)
    {
        res.push_back({st, ed});
    }
    sect = res;
}

signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int a, b;
        cin >> a >> b;
        sect.push_back({a, b}); // 存储所有区间
    }
    merge(sect); // 合并区间
    
    int res = 0;
    for (auto seg : sect) // 计算总长度
    {
        res += seg.second - seg.first;
    }
    cout << res << endl;
    return 0;
}

五、重点细节

  1. 区间表示:题目中的区间是左闭右开[a,b),所以长度直接计算为b-a
  2. 初始化值:合并算法中初始st和ed设为极小值(-1e10),用于判断是否是第一个区间
  3. 排序处理:必须先对所有区间按左端点排序,才能正确合并
  4. 边界处理:最后一个合并后的区间需要在循环结束后单独加入结果集

六、复杂度分析

  1. 时间复杂度
    • 排序操作:O(n log n)
    • 合并操作:O(n)
    • 总复杂度:O(n log n)(排序占主导)
  2. 空间复杂度
    • 存储区间:O(n)
    • 合并结果:最坏情况O(n)(无任何区间可合并时)

七、总结

本题考察了区间合并这一经典算法,关键在于:

  1. 理解如何判断和处理区间重叠
  2. 掌握排序预处理的重要性
  3. 注意边界条件的处理(如最后一个区间)
  4. 理解左闭右开区间的长度计算方式

该解法高效且清晰,能够正确处理题目中的所有情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hongjianMa

感恩社区,回馈社区

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值