题解:学而思编程 路灯维修
【题目来源】
学而思编程:路灯维修
【题目描述】
某条街道长度为 \(l\) 米,街道的一端在 \(0\) 米处,另一端在 \(l\) 米处。街道上的每个整数点位置 \((0,1,2,…,l)\) 都安装了一盏路灯,一共 \(l+1\) 盏路灯。
由于线路老化,某些连续路段的路灯会发生故障不亮(包括路段两端点的路灯)。这些故障路段由维修人员检测出来,每个路段用起始点和终止点表示,不同路段之间可能有重叠。
现在请你计算,在所有故障路段统计完成后,街道上还有多少盏路灯能正常亮着。
【输入】
第一行两个整数 \(l,m\),分别表示:\(l\):街道长度(米),路灯位置从 \(0\) 到 \(l\);\(m\):故障路段的数量
接下来 \(m\) 行,每行两个整数 \(u,v\),表示一个故障路段的起始点和终止点(\(0≤u≤v≤l\))。
【输出】
输出一个整数,表示能正常亮着的路灯数量。
【输入样例】
50 3
15 30
10 20
40 41
【输出样例】
28
【核心思想】
-
问题分析:给定街道长度 \(l\),路灯位于 \(0\) 到 \(l\) 的整数点。\(m\) 个故障路段 \([u, v]\) 可能重叠,求正常亮着的路灯数量。这是一个区间覆盖 + 差分标记问题,关键在于高效标记所有故障路灯并统计未故障的数量。
-
算法选择:
- 直接标记:用布尔数组
f[i]标记位置 \(i\) 的路灯是否故障 - 线性枚举:对每个故障路段,遍历区间内所有位置进行标记
- 统计答案:遍历所有位置,统计未被标记的路灯数
- 直接标记:用布尔数组
-
关键步骤:
- 初始化:读取 \(l\)(街道长度)和 \(m\)(故障路段数)
- 标记故障路灯:
- 遍历 \(m\) 个故障路段:
- 读取 \(u\) 和 \(v\)
- 遍历 \(i\) 从 \(u\) 到 \(v\):
f[i] = 1(标记为故障)
- 遍历 \(m\) 个故障路段:
- 统计正常路灯:
ans = 0- 遍历 \(i\) 从 \(0\) 到 \(l\):
- 若
f[i] == 0:ans++
- 若
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(m \times l)\),最坏情况每个故障路段覆盖整个街道
- 空间复杂度:\(O(l)\),标记数组
f[0..l]
-
直接标记的核心思想:
- 重叠处理:多个故障路段覆盖同一位置时,重复标记不影响结果(
f[i] = 1幂等) - 简单直观:对于 \(l \leq 10^4\) 的数据范围,直接枚举完全可行,无需复杂优化
- 离散化适用:路灯位置为整数点且范围有限,数组下标直接对应位置
- 扩展优化:若 \(l\) 很大(如 \(10^9\)),可用差分数标或区间合并,但本题范围小,直接标记更简洁
- 适用于"有限范围 + 区间覆盖 + 统计补集"类问题,直接标记在数据范围适中时是最简单的方案
- 重叠处理:多个故障路段覆盖同一位置时,重复标记不影响结果(
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 10005; // 定义最大长度
int l, m, f[N]; // 道路长度l,操作次数m,标记数组f
int main() // 主函数
{
cin >> l >> m; // 输入道路长度和操作次数
while (m--) // 处理每个操作
{
int u, v; // 区间起点u,终点v
cin >> u >> v; // 输入要移除的区间
for (int i=u; i<=v; i++) // 标记区间内所有树
f[i] = 1; // 标记为已移除
}
int ans = 0; // 剩余树的数量
for (int i=0; i<=l; i++) // 遍历所有位置
if (f[i]==0) ans++; // 统计未标记的树
printf("%d\n", ans); // 输出剩余树的数量
return 0; // 程序正常结束
}
【运行结果】
50 3
15 30
10 20
40 41
28
浙公网安备 33010602011771号