这道题目描述了一个有趣的物理场景:一个人在房间里走动,灯泡和墙壁之间的距离固定,我们需要计算人影子的最大可能长度。这个看似简单的问题实际上考察了我们对几何光学和极值问题的理解。
我们有三个关键参数:
我们需要找出人在灯泡和墙壁之间移动时,影子长度的最大值。
当人站在距离灯泡x的位置时,会出现两种情况:
这两种情况的分界点是当人的头部、灯泡和墙角三点共线时,即x = h*D/H。
对于第一种情况(x ≤ h*D/H):
影子长度L = x + (H - (H-h)*D/(D-x))
对于第二种情况(x > h*D/H):
影子长度L = (x - D) + (D - x)*H/(H - h)
我们需要在区间[0, D]上寻找函数L(x)的最大值。观察函数性质可以发现:
三分法非常适合求解这种单峰函数的极值问题,因为它能有效地缩小搜索范围。
三分法的核心思想是将搜索区间分成三部分,通过比较中间两个点的函数值来缩小搜索范围。
cpp复制void three_cut() {
double l = 0, r = D; // 初始搜索区间
while(r - l >= 1e-7) { // 设置足够小的精度阈值
double lmid = l + (r - l)/3;
double rmid = r - (r - l)/3;
if(ask(lmid) > ask(rmid)) {
r = rmid; // 舍弃右半部分
} else {
l = lmid; // 舍弃左半部分
}
}
printf("%.3lf\n", ask(l));
}
注意:在实际实现中,我们使用了1e-7作为精度阈值,这比题目要求的1e-3更严格,确保结果的准确性。
cpp复制int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%lf%lf%lf", &H, &h, &D);
three_cut();
}
return 0;
}
主函数负责处理输入输出,读取测试用例数量T,然后对每组数据调用三分法求解。
cpp复制double ask(double x) {
if(x <= h*D/H)
return x + (H - (H-h)*D/(D-x));
else
return x - D + ((D - x)*H/(H - h));
}
这个函数实现了我们前面推导的两种情况的数学表达式,根据x的位置选择正确的计算公式。
由于题目要求输出结果与标准答案的绝对误差不超过1e-3,我们需要:
虽然题目保证了H > h,但在实际编程中还是应该注意:
三分法的时间复杂度为O(log(D/ε)),其中ε是精度要求。对于题目给定的约束条件(T ≤ 100),这个算法效率完全足够。
让我们验证题目提供的样例:
输入:2 1 0.5
计算过程:
输入:2 0.5 3
计算过程:
输入:4 3 4
计算过程:
我们还应该测试一些边界情况:
这些测试可以帮助我们发现潜在的数值稳定性问题。
可能的原因:
调试建议:
虽然本题不需要特别优化,但可以考虑:
如果遇到三分法无法收敛的情况:
除了三分法,这个问题还可以用:
类似的极值问题在工程中很常见,如:
掌握三分法这类数值方法对解决实际问题很有帮助。
对于算法竞赛代码:
在实际比赛中,我通常会先写出正确但可能不够优化的代码,确保逻辑正确后再考虑优化。过早优化往往会导致代码复杂且容易出错。
这道题目很好地结合了物理直觉和算法实现,通过三分法求解极值问题的思路在很多场景下都适用。理解背后的数学原理比单纯记住算法模板更重要,这能帮助我们在遇到新问题时灵活应用所学知识。