洛谷 - P1496 火烧赤壁
题目链接
考察知识点
- 算法 - 模拟
思路分析
考虑用模拟的方法解决这道题。
先把读入的线段按照左端小在前的原则进行排序,显然这样排序产生的答案必定正确。
对所有线段进行区间合并,统计合并后的每个线段长度之和。
注意:数据中的线段左闭右开。
时间复杂度
\(O(n)\)
C++代码
// Problem: P1496 火烧赤壁
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1496
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <cstdio> // 包含标准输入输出函数库
#include <algorithm> // 包含排序等算法函数库
#include <vector> // 包含向量容器类库
using namespace std; // 引入标准命名空间
// 定义一个别名PII,表示成对的整数(用于存储区间的起点和终点)
typedef pair<int, int> PII;
// 定义一个向量容器segs,用于存储所有的着火区间信息
vector<PII> segs;
// 定义整数n,用于存储着火信息的条数
int n;
//合并重叠或相邻的区间
void merge(vector<PII> &segs) {
// 先将所有区间按照起点从小到大排序
sort(segs.begin(), segs.end());
// 定义一个临时向量res,用于存储合并后的区间
vector<PII> res;
// 初始化当前区间的起点st和终点ed(使用一个极小值作为初始值)
int st = -2e9, ed = -2e9;
// 遍历每一个区间
for (auto seg : segs) {
// 取出当前区间的起点l和终点r
int l = seg.first, r = seg.second;
// 如果当前区间的起点大于上一个区间的终点,说明两个区间不重叠
if (ed < l) {
// 如果不是初始区间(避免添加默认的极小值区间),则将上一个区间加入结果
if (st != -2e9) res.push_back({st, ed});
// 更新当前区间为新的区间
st = l, ed = r;
} else {
// 如果区间重叠或相邻,则合并区间(更新终点为两个区间终点的最大值)
ed = max(ed, r);
}
}
// 循环结束后,将最后一个区间加入结果(避免遗漏)
if (st != -2e9) res.push_back({st, ed});
// 将合并后的结果赋值给原向量
segs = res;
}
int main() {
// 读取着火信息的条数n
scanf("%d", &n);
// 循环读取n条着火区间信息
for (int i = 1; i <= n; i++) {
int l, r;
// 读取区间的起点l和终点r(题目中是左闭右开区间)
scanf("%d%d", &l, &r);
// 将左闭右开区间转换为左闭右闭区间(方便计算长度),并加入到segs中
// 原区间[a, b)等价于[a, b-1](闭区间)
segs.push_back({l, r - 1});
}
// 调用merge函数合并重叠的区间
merge(segs);
// 定义变量ans,用于存储最终的燃烧总长度
int ans = 0;
// 遍历合并后的所有区间,计算总长度
for (auto seg : segs) {
// 对于左闭右闭区间[st, ed],长度为ed - st + 1
ans += seg.second + 1 - seg.first;
}
// 输出计算得到的总长度
printf("%d\n", ans);
return 0;
}