2026华为OD面试题009:路灯照明问题

题目描述

一条笔直的公路上装了 N 个路灯,从位置 0 开始,路灯之间间距固定为 100 米。

每个路灯有自己的照明半径。请计算第一个路灯和末尾一个路灯之间,无法照明的区间的长度和

N 不超过 10 万,照明半径最大为 10^7

讲个故事:夜跑爱好者的噩梦

小李喜欢夜跑。这条路上每隔 100 米有一盏路灯,但亮度不一,有的照得远,有的照得近。

他想知道:从第一盏路灯跑到末尾一盏,路上有多少段黑漆漆的地方?

这不就是把所有光照范围画出来,看看中间漏了多少缝吗?

核心原理:区间覆盖求 gaps

路灯位置是固定的:0,100,200,…,(N - 1) * 100

第 i 盏路灯的照明范围是:

[i * 100 - r[i], i * 100 + r[i]]

把所有区间合并后,用总长度减去被覆盖的长度,就是答案。

更直接的做法:

  1. 生成所有区间
  2. 按左端点排序
  3. 逐个合并,记录合并后的覆盖范围
  4. 合并过程中,相邻覆盖区间之间的缝隙就是未覆盖长度

怎么实现?

  1. 读入 N 和半径数组 r
  2. 生成区间 [i*100 - r[i], i*100 + r[i]]
  3. 按左端点排序
  4. 维护当前合并区间 [curL, curR]
    • 如果下一个区间左端点 <= curR,说明有重叠,更新 curR = max(curR, right)
    • 否则,说明出现缝隙,ans += 缝隙长度,然后开启新区间
  5. 注意只统计 [0, (N-1)*100] 范围内的缝隙

代码实现

C 语言

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    long long l, r;
} Interval;

int cmp(const void *a, const void *b) {
    Interval *x = (Interval *)a, *y = (Interval *)b;
    if (x->l != y->l) return x->l < y->l ? -1 : 1;
    return x->r < y->r ? -1 : 1;
}

long long solve(int n, long long r[]) {
    Interval ivs[100005];
    for (int i = 0; i < n; i++) {
        long long pos = i * 100LL;
        ivs[i].l = pos - r[i];
        ivs[i].r = pos + r[i];
    }
    qsort(ivs, n, sizeof(Interval), cmp);

    long long totalLen = (n - 1) * 100LL;
    long long covered = 0;
    long long curL = ivs[0].l, curR = ivs[0].r;

    for (int i = 1; i < n; i++) {
        if (ivs[i].l <= curR) {
            if (ivs[i].r > curR) curR = ivs[i].r;
        } else {
            covered += curR - curL;
            curL = ivs[i].l;
            curR = ivs[i].r;
        }
    }
    covered += curR - curL;

    // 只考虑 [0, totalLen] 区间
    if (curR > totalLen) covered -= curR - totalLen;
    if (ivs[0].l < 0) covered -= 0 - ivs[0].l;

    return totalLen - covered;
}

C++

#include <bits/stdc++.h>
using namespace std;

long long solve(int n, vector<long long> r) {
    vector<pair<long long, long long>> ivs;
    for (int i = 0; i < n; i++) {
        long long pos = i * 100LL;
        ivs.push_back({pos - r[i], pos + r[i]});
    }
    sort(ivs.begin(), ivs.end());

    long long totalLen = (n - 1) * 100LL;
    long long curL = ivs[0].first, curR = ivs[0].second;
    long long covered = 0;

    for (int i = 1; i < n; i++) {
        auto [l, rr] = ivs[i];
        if (l <= curR) {
            curR = max(curR, rr);
        } else {
            covered += curR - curL;
            curL = l;
            curR = rr;
        }
    }
    covered += curR - curL;

    // 裁剪到 [0, totalLen]
    covered = min(covered, totalLen);
    return totalLen - covered;
}

Java

import java.util.*;

public class Main {
    public static long solve(int n, long[] r) {
        long[][] ivs = new long[n][2];
        for (int i = 0; i < n; i++) {
            long pos = i * 100L;
            ivs[i][0] = pos - r[i];
            ivs[i][1] = pos + r[i];
        }
        Arrays.sort(ivs, Comparator.comparingLong(a -> a[0]));

        long totalLen = (n - 1) * 100L;
        long curL = ivs[0][0], curR = ivs[0][1];
        long covered = 0;

        for (int i = 1; i < n; i++) {
            long l = ivs[i][0], rr = ivs[i][1];
            if (l <= curR) {
                curR = Math.max(curR, rr);
            } else {
                covered += curR - curL;
                curL = l;
                curR = rr;
            }
        }
        covered += curR - curL;

        // 限制在 [0, totalLen] 内
        long ans = totalLen - covered;
        return Math.max(ans, 0);
    }
}

JavaScript

function solve(n, r) {
    const ivs = [];
    for (let i = 0; i < n; i++) {
        const pos = i * 100;
        ivs.push([pos - r[i], pos + r[i]]);
    }
    ivs.sort((a, b) => a[0] - b[0]);

    const totalLen = (n - 1) * 100;
    let curL = ivs[0][0], curR = ivs[0][1];
    let covered = 0;

    for (let i = 1; i < n; i++) {
        const [l, rr] = ivs[i];
        if (l <= curR) {
            curR = Math.max(curR, rr);
        } else {
            covered += curR - curL;
            curL = l;
            curR = rr;
        }
    }
    covered += curR - curL;

    return Math.max(totalLen - covered, 0);
}

Python

def solve(n, r):
    ivs = []
    for i in range(n):
        pos = i * 100
        ivs.append((pos - r[i], pos + r[i]))
    ivs.sort()

    total_len = (n - 1) * 100
    cur_l, cur_r = ivs[0]
    covered = 0

    for l, rr in ivs[1:]:
        if l <= cur_r:
            cur_r = max(cur_r, rr)
        else:
            covered += cur_r - cur_l
            cur_l, cur_r = l, rr
    covered += cur_r - cur_l

    return max(total_len - covered, 0)

复杂度分析

  • 生成区间:O(n)
  • 排序:O(n * log n)
  • 合并区间:O(n)
  • 总时间复杂度:O(n * log n)
  • 空间复杂度:O(n)

总结一下

路灯照明问题 = 区间合并求未覆盖长度。

套路:

  1. 把每个路灯的照明范围转成区间
  2. 按左端点排序
  3. 合并重叠区间
  4. 用总长度减覆盖长度

注意边界:题目只关心第一盏到末尾一盏路灯之间的区间,别被路灯照到外面去的长度干扰。

你平时遇到区间问题,是先排序还是先扫描?欢迎在评论区聊聊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

令狐掌门

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值