题解:学而思编程 路灯维修

【题目来源】

学而思编程:路灯维修

【题目描述】

某条街道长度为 \(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

【核心思想】

  1. 问题分析:给定街道长度 \(l\),路灯位于 \(0\)\(l\) 的整数点。\(m\) 个故障路段 \([u, v]\) 可能重叠,求正常亮着的路灯数量。这是一个区间覆盖 + 差分标记问题,关键在于高效标记所有故障路灯并统计未故障的数量。

  2. 算法选择

    • 直接标记:用布尔数组 f[i] 标记位置 \(i\) 的路灯是否故障
    • 线性枚举:对每个故障路段,遍历区间内所有位置进行标记
    • 统计答案:遍历所有位置,统计未被标记的路灯数
  3. 关键步骤

    • 初始化:读取 \(l\)(街道长度)和 \(m\)(故障路段数)
    • 标记故障路灯
      • 遍历 \(m\) 个故障路段:
        • 读取 \(u\)\(v\)
        • 遍历 \(i\)\(u\)\(v\)f[i] = 1(标记为故障)
    • 统计正常路灯
      • ans = 0
      • 遍历 \(i\)\(0\)\(l\)
        • f[i] == 0ans++
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(m \times l)\),最坏情况每个故障路段覆盖整个街道
    • 空间复杂度:\(O(l)\),标记数组 f[0..l]
  5. 直接标记的核心思想

    • 重叠处理:多个故障路段覆盖同一位置时,重复标记不影响结果(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
posted @ 2026-07-05 14:40  团爸讲算法  阅读(6)  评论(0)    收藏  举报