P1496 火烧赤壁
文章目录
- 题目背景
- 题目描述
- 输入格式:
- 输出格式:
- 数据范围
- 输入样例
- 输出样例
- 方法:区间合并
- 解题思路
- 代码
- 复杂度分析:
题目背景
曹操平定北方以后,公元 208 年,率领大军南下,进攻刘表。他的人马还没有到荆州,刘表已经病死。他的儿子刘琮听到曹军声势浩大,吓破了胆,先派人求降了。
孙权任命周瑜为都督,拨给他三万水军,叫他同刘备协力抵抗曹操。
隆冬的十一月,天气突然回暖,刮起了东南风。
没想到东吴船队离开北岸大约二里距离,前面十条大船突然同时起火。火借风势,风助火威。十条火船,好比十条火龙一样,闯进曹军水寨。那里的船舰,都挤在一起,又躲不开,很快地都烧起来。一眨眼工夫,已经烧成一片火海。
曹操气急败坏的把你找来,要你钻入火海把连环线上着火的船只的长度统计出来!
题目描述
给定每个起火部分的起点和终点,请你求出燃烧位置的长度之和。
输入格式:
第一行一个整数,表示起火的信息条数 n。
接下来 n 行,每行两个整数 a, b,表示一个着火位置的起点和终点(注意:左闭右开)。
输出格式:
输出一行一个整数表示答案。
数据范围
- 1 ≤ n ≤ 2 × 1 0 4 1≤n≤2\times10^4 1≤n≤2×104
- − 2 31 ≤ a ≤ b < 2 31 -2^{31} \leq a \leq b \lt 2^{31} −231≤a≤b<231
输入样例
3
-1 1
5 11
2 9
输出样例
11
方法:区间合并
解题思路
- 按区间的左端点进行从小到大的排序
- 我们把维护区间的左端点记为指针 st,右端点记为指针 ed
每次搜索一个新的区间,它与维护区间之间有 3 种情况:
- 新区间是维护区间的子区间
- 新区间与维护区间有相交部分
- 新区间与维护区间没有相交部分
依据新区间和维护区间是否有相交的部分,可以将上述 3 种情况视为 2 种情形。
如果新区间的左端点 > 维护区间的右端点,则两个区间没有相交部分,则将维护区间的左右端点保存起来;
如果新区间的左端点 <= 维护区间的右端点,则两个区间有相交部分,则更新维护区间的右端点,取两个区间右端点的最大值。
Tips
- sort 在 c++ 中,会优先对左端点进行从小到大的排序,再对右端点进行从小到大的排序。
- 将 vector 容器作为函数参数,使用引用传递的方式,在形式参数前加上 &,函数内部形参发生变化时,这种改变可以直接传递给被调用的实参。
- 注意 st != -2e9,把 -2e9 换成再小一点的数就会报错,有哪位大佬懂得可以告诉我吗?
代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<long,long> PII;
vector<PII> segs;
int n;
void merge(vector<PII> &segs) {
vector<PII> res;
sort(segs.begin(), segs.end());
long 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(seg.second, ed);
}
if(st != -2e9) res.push_back({st, ed});
segs = res;
}
int main() {
cin >> n;
while(n--) {
long l, r;
cin >> l >> r;
segs.push_back({l, r});
}
merge(segs);
int sum = 0;
for(auto seg : segs) {
sum += seg.second - seg.first;
}
cout << sum;
return 0;
}
复杂度分析:
- 时间复杂度: O ( n × l o g 2 n ) O(n\times log_2n) O(n×log2n)
- 空间复杂度: O ( n ) O(n) O(n)