一本通题解——1438:灯泡

14 篇文章 2 订阅
12 篇文章 2 订阅

题目链接

一本通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、影子并未碰到墙壁。如下图所示,这需要使用三角形的相似来解决。

如上图所示的情况,此时的相似比为 \frac{H-h}{h},其中 h 表示 Mildleopard 的身高,那么影子的长度即为 \frac{x\ast h}{H-h}

2、影子有一部分在墙壁上。如题目描述给的图所示。这就需要用两次相似来求影子在墙壁上的长度,如下图所示。

如上图所示的情况,利用直角构造两个三角形,当 Mildleopard 站在离灯泡 x 水平距离时,他与墙的距离即为D−x,那么这两个相似三角形的相似比即为 \frac{x}{D-x} 。我们知道灯泡的高度与 Mildleopard 高度差为 H-h ,然后就可以计算出下面的三角形的较短边的长度为 \frac{H-d}{\frac{x}{d-x}}=\frac{(H-d)\ast (D-x)}{x},这样我们就可以算出在墙上的影子的长度为h-\frac{(H-d)\ast (D-x)}{x},那么整个影子的长度为 D-x+h-\frac{(H-d)\ast (D-x)}{x}

很明显,影子的两种可能都是单调的,合起来就是一个单峰函数。

算法思路

三分查找

那么问题就简单了,因为是浮点数计算,构造一个三分查找,注意要卡一下精度。套用标准浮点数三分查找模板,从 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;
}

直接计算

我们进一步用数学进行分析。假设图中对应的角度为 \ss,如果墙上有影子,那么函数关系可以推导出D+H-((H-h)/tan\ss +D\ast tan\ss )。问题就变成如何求极值问题,那么有以下几种关系:

1、如果 \sqrt{\frac{H-h}{D}}\geqslant \frac{H}{D} ,答案为 h\ast \frac{D}{H}

2、如果 \sqrt{\frac{H-h}{D}}\leqslant \frac{H-h}{D} ,答案为 h

3、否则答案为 D+H-2\ast \sqrt{(H-h)\ast D}

那么对应的代码就更简单了。

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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

努力的老周

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

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

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

打赏作者

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

抵扣说明:

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

余额充值