题目概述
有一根长len的木棍,加热了n度,长度会膨胀为len*(1+n*c),c为膨胀系数。现在把这根木棍夹在两堵墙之间,木棍会向上弯曲变成弧形,求弧形中点和原木棍中点的高度差。
解题报告
一道典型的二分题目,我们会想到直接二分答案dis,然后就可以计算出这个弧所在的圆的半径,紧接着就可以求出弧长。用求出的弧长与真正的弧长做对比来确定下一次二分的范围。
但是有个问题,怎么保证算出的弧长满足单调性?可以yy一下得到(我蒟蒻,并不会数学严格证明:P)。
1.dis小的时候:
2.dis大的时候:
在木棍原长度不变的情况下,dis越大,显然木棍越膨胀(yy……),所以弧长也就越大,满足单调性。
满足了单调性之后,我们就可以安心二分了,这里再详细说明一下如何求弧长:
根据勾股定理,我们知道(R-dis)^2+(len/2)^2=R^2,所以:
R^2-2*R*dis+dis^2+len^2/4=R^2
2*R*dis=dis^2+len^2/4
R=(dis+len^2/4/dis)/2
根据len’=2*θ*R=2*acos((R-dis)/R)*R(acos表示反三角函数,即知道cos值求角度(弧度制)),就可以得到弧长。
二分的范围:弧最多是半圆,所以dis最多是len/2。
特殊:当len=0或n=0或C=0时,答案就是0,最好特判掉。否则可能会造成被0除错误。
示例程序
#include<cstdio>
#include<cmath>
using namespace std;
const double PI=3.14159265358;
double len,n,C,len_; //len为原长度,len_为膨胀后的长度
bool check(double dis)
{
double R=(dis+len*len/4/dis)/2;
return 2*acos((R-dis)/R)*R<=len_; //如果<=len_就验证成功
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%lf%lf%lf",&len,&n,&C);
while (len!=-1||n!=-1||C!=-1)
{
if (!len||!n||!C) {printf("0.000\n");scanf("%lf%lf%lf",&len,&n,&C);continue;} //特判
double L=1e-5,R=len/2,mid;len_=(n*C+1)*len;
while (L<=R)
{
mid=(L+R)/2;
if (check(mid)) L=mid+(1e-5); else R=mid-(1e-5); //验证成功就大一点,否则小一点
}
printf("%.3f\n",R);
scanf("%lf%lf%lf",&len,&n,&C);
}
return 0;
}