根据相似三角形建立人位置到影子长度的分段单峰函数,利用三分查找求最大影子长度。
OJ: roj
题目 ID: 1438
难度:普及
标签:三分计算几何
创建: 2026-09-30 10:22
更新: 2026-10-05 23:47
题目描述
高度
样例输入 3 / 2 1 0.5 / 2 0.5 3 / 4 3 4(/ 表示换行),样例输出 1.000 / 0.750 / 4.000。
思路
设人距灯
参考代码
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;
}