题解:洛谷 P1496 火烧赤壁
【题目来源】
【题目描述】
曹操平定北方以后,公元 \(208\) 年,率领大军南下,进攻刘表。他的人马还没有到荆州,刘表已经病死。他的儿子刘琮听到曹军声势浩大,吓破了胆,先派人求降了。
孙权任命周瑜为都督,拨给他三万水军,叫他同刘备协力抵抗曹操。
隆冬的十一月,天气突然回暖,刮起了东南风。
没想到东吴船队离开北岸大约二里距离,前面十条大船突然同时起火。火借风势,风助火威。十条火船,好比十条火龙一样,闯进曹军水寨。那里的船舰,都挤在一起,又躲不开,很快地都烧起来。一眨眼工夫,已经烧成一片火海。
曹操气急败坏的把你找来,要你钻入火海把连环线上着火的船只的长度统计出来!
给定每个起火部分的起点和终点,请你求出燃烧位置的长度之和。
【输入】
第一行一个整数,表示起火的信息条数 \(n\)。
接下来 \(n\) 行,每行两个整数 \(a, b\),表示一个着火位置的起点和终点(注意:左闭右开)。
【输出】
输出一行一个整数表示答案。
【输入样例】
3
-1 1
5 11
2 9
【输出样例】
11
【算法标签】
《洛谷 P1496 火烧赤壁》 #离散化# #O2优化#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
int n; // 线段数量
int st, ed; // 当前合并区间的起点和终点
int sum; // 合并后的总长度
// 线段结构体
struct Line
{
int l, r; // 线段的起点和终点
// 重载小于运算符,用于排序
bool operator < (Line &t)
{
return l < t.l;
}
} a[20005]; // 存储所有线段
signed main()
{
// 输入线段数量
cin >> n;
// 输入每条线段的起点和终点
for (int i = 1; i <= n; i++)
{
cin >> a[i].l >> a[i].r;
}
// 按线段起点排序
sort(a + 1, a + n + 1);
// 初始化第一个合并区间
st = a[1].l;
ed = a[1].r;
sum += a[1].r - a[1].l;
// 遍历处理每条线段
for (int i = 2; i <= n; i++)
{
// 如果当前线段与合并区间有重叠
if (a[i].l <= ed)
{
// 完全被包含则跳过
if (a[i].r < ed)
{
continue;
}
// 否则扩展合并区间
else
{
st = ed; // 更新起点为原终点
ed = a[i].r; // 更新终点为新线段的终点
sum += ed - st; // 增加扩展部分的长度
}
}
// 如果没有重叠,创建新的合并区间
else
{
st = a[i].l;
ed = a[i].r;
sum += ed - st;
}
}
// 输出合并后的总长度
cout << sum << endl;
return 0;
}
// 使用acwing模板二刷
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
const int N = 2005; // 定义最大线段数量
typedef pair<int, int> PII; // 定义线段类型为pair
int n; // 线段数量
int sum; // 合并后的总长度
vector<PII> segs; // 存储所有线段的容器
/**
* 合并重叠或相邻的线段
* @param 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; // 更新线段集合为合并后的结果
}
signed main()
{
// 输入线段数量
cin >> n;
// 输入每条线段的起点和终点
for (int i = 1; i <= n; i++)
{
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}
// 合并线段
merge(segs);
// 计算合并后的总长度
for (int i = 0; i < segs.size(); i++)
sum += segs[i].second - segs[i].first;
// 输出结果
cout << sum << endl;
return 0;
}
【运行结果】
3
-1 1
5 11
2 9
11
浙公网安备 33010602011771号