题目链接
一本通OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1438。
我的OJ:http://47.110.135.197/problem.php?id=4473。
题目
题目描述
相比 Wildleopard 的家,他的弟弟 Mildleopard 比较穷。他的房子是狭窄的,而且在他的房间里仅有一个灯泡。每天晚上,他徘徊在自己狭小的房子里,思考如何赚更多的钱。有一天,他发现他的影子的长度随着他在灯泡和墙壁之间走动时会发生变化。一个突然的想法出现在他的脑海里,他想知道在房间里他的影子的最大长度。
输入
输入文件的第一行包含一个整数 T,表示测试数据的组数。
对于每组测试数据仅有一行包含三个实数 H,h 和 D,H 表示灯泡的高度,h 表示 Mildleopard 的身高,D 表示灯泡和墙的水平距离。
输出
输出文件共 T 行,每组数据占一行,表示影子的最大长度,保留三位小数。
样例输入
3
2 1 0.5
2 0.5 3
4 3 4
样例输出
1.000
0.750
4.000
数据范围
T ≤ 100,0.01 ≤ H, h, D ≤1000,0.01 ≤ H − h。
分析
数学相关
相似三角形。
题目分析
首先这是一个数学题,求的是影子长度。我们来讨论一个影子的可能:
1、影子并未碰到墙壁。如下图所示,这需要使用三角形的相似来解决。
如上图所示的情况,此时的相似比为 ,其中 h 表示 Mildleopard 的身高,那么影子的长度即为 。
2、影子有一部分在墙壁上。如题目描述给的图所示。这就需要用两次相似来求影子在墙壁上的长度,如下图所示。
如上图所示的情况,利用直角构造两个三角形,当 Mildleopard 站在离灯泡 x 水平距离时,他与墙的距离即为D−x,那么这两个相似三角形的相似比即为 。我们知道灯泡的高度与 Mildleopard 高度差为 H-h ,然后就可以计算出下面的三角形的较短边的长度为 ,这样我们就可以算出在墙上的影子的长度为,那么整个影子的长度为 。
很明显,影子的两种可能都是单调的,合起来就是一个单峰函数。
算法思路
三分查找
那么问题就简单了,因为是浮点数计算,构造一个三分查找,注意要卡一下精度。套用标准浮点数三分查找模板,从 0 到 D 的范围内查找,实现 check 函数即可。
AC 参考代码
#include <bits/stdc++.h>
using namespace std;
double H, h, D;//由于check函数需要用的,就用全局变量吧
double check(double x) {
if ((H-h)/x*(D-x)>h) {
//灯的高度小于影子到墙底部距离。属于第二种情况
return x/(H-h)*h;
} else {
//属于第一种情况
return D-x+h-(H-h)/x*(D-x);
}
}
int main() {
int t;
cin>>t;
int i, j;
for (i=0; i<t; i++) {
cin >> H >> h >> D;
//三分查找
double eps = 1e-8;
double left=0;
double right=D;
double l_mid, r_mid;
while (left+eps<right) {
l_mid = left+(right-left)/3;
r_mid = right-(right-left)/3;
if (check(l_mid) > check(r_mid)) {
right = r_mid;
} else {
left = l_mid;
}
}
printf("%.3lf\n", check(left));
}
return 0;
}
直接计算
我们进一步用数学进行分析。假设图中对应的角度为 ,如果墙上有影子,那么函数关系可以推导出。问题就变成如何求极值问题,那么有以下几种关系:
1、如果 ,答案为 。
2、如果 ,答案为 。
3、否则答案为 。
那么对应的代码就更简单了。
AC 参考代码
#include <cstdio>
#include <cmath>
int main() {
int t;
scanf("%d", &t);
for (int i=0; i<t; i++) {
double H, h, D;
scanf("%lf %lf %lf", &H, &h, &D);
double ans = D-h*D/H;
double val = sqrt((H-h)*D);
if (ans<=val && D>val) {
ans = D+H-2*val;
} else if (val<ans) {
ans = h*D/H;
} else {
ans = h;
}
printf("%.3lf\n", ans);
}
return 0;
}