「一本通 1.2 练习 3」灯泡

GitHub跳转原题关系图返回列表

根据相似三角形建立人位置到影子长度的分段单峰函数,利用三分查找求最大影子长度。

OJ: roj

题目 ID: 1438

难度:普及

标签:三分计算几何

创建: 2026-09-30 10:22

更新: 2026-10-05 23:47

题目描述

高度 hh 的人站在灯泡与墙之间:灯高 HH(H>hH>h),灯到墙水平距离 DD,人距灯 xx,灯光过人头顶投到地面或墙上形成影子(地面与墙面部分之和)。求影子最大长度,保留三位小数。输入:第一行整数 TT,随后每行三个实数 H,h,DH, h, D;输出:每组一行答案。数据范围:T≤100T \le 100,10−2≤H,h,D≤10310^{-2} \le H, h, D \le 10^3,H−h≥10−2H-h \ge 10^{-2}。

样例输入 3 / 2 1 0.5 / 2 0.5 3 / 4 3 4(/ 表示换行),样例输出 1.000 / 0.750 / 4.000。

思路

设人距灯 xx,影子长度 L(x)L(x) 是单峰函数:光线交点未到墙角(x≤x0=D(H−h)Hx \le x_0=\frac{D(H-h)}{H})时影子全在地面,L(x)=hxH−hL(x)=\frac{hx}{H-h} 单调递增;之后影子爬上墙面,L(x)=D−x+H−D(H−h)xL(x)=D-x+H-\frac{D(H-h)}{x} 是凹函数先增后减。直接在 [0,D][0,D] 上三分 L(x)L(x) 取最大值,迭代足够多次即可满足精度。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-07-05 21:47
# update_at: 2026-07-05 21:47

import sys


def shadow_len(x: float, H: float, h: float, D: float) -> float:
    """计算人距灯泡水平距离为 x 时的总影子长度(地面部分 + 墙面部分)。"""
    split_x = D * (H - h) / H
    return x * h / (H - h) if x <= split_x else D - x + H - D * (H - h) / x


def solve() -> None:
    data = sys.stdin.read().split()
    if not data:
        return
    it = iter(data)
    test_cases = int(next(it))

    for _ in range(test_cases):
        H, h, D = float(next(it)), float(next(it)), float(next(it))
        low, high = 0.0, D
        for _ in range(80):
            m1 = low + (high - low) / 3.0
            m2 = high - (high - low) / 3.0
            if shadow_len(m1, H, h, D) < shadow_len(m2, H, h, D):
                low = m1
            else:
                high = m2
        print(f"{shadow_len(low, H, h, D):.3f}")


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 23:47
 * update_at: 2026-10-05 23:47
 */
// 灯泡影子问题:影子长度是人位置 x 的单峰函数,三分查找求最大值。
#include <cstdio>
#include <cmath>

typedef long long ll;

// 全局变量:H 灯高,h 人身高,D 灯到墙的水平距离
double H, h, D;

// 计算人站在距光源 x 处时的影子总长度(地面部分 + 墙面部分)
double shadow_len(double x) {
    // 光线过头顶,与地面交点距光源为 H*x/(H-h)
    if (H * x / (H - h) <= D) {
        // 影子全在地面:交点减去人所在位置
        return h * x / (H - h);
    }
    // 影子爬上墙:地面剩 D-x,墙上的高为 H - D(H-h)/x,加起来
    return D - x + H - D * (H - h) / x;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%lf%lf%lf", &H, &h, &D);
        // 三分:每次比较区间两个三等分点,向峰值一侧收缩
        double left = 0.0, right = D;
        for (int i = 0; i < 100; i++) {
            double m1 = left + (right - left) / 3;
            double m2 = right - (right - left) / 3;
            if (shadow_len(m1) < shadow_len(m2))
                left = m1;
            else
                right = m2;
        }
        printf("%.3f\n", shadow_len(left));
    }
    return 0;
}