一道非常有意思的二分题
描述
当长度为L的一根细木棍的温度升高n度,它会膨胀到新的长度L'=(1+n*C)*L,其中C是热膨胀系数。
当一根细木棍被嵌在两堵墙之间被加热,它将膨胀形成弓形的弧,而这个弓形的弦恰好是未加热前木棍的原始位置。
你的任务是计算木棍中心的偏移距离。
输入
三个非负实数:木棍初始长度(单位:毫米),温度变化(单位:度),以及材料的热膨胀系数。
保证木棍不会膨胀到超过原始长度的1.5倍。
输出
木棍中心的偏移距离(单位:毫米),保留到小数点后第三位。
样例输入
1000 100 0.0001
样例输出
61.329
来源
Waterloo local 2004.06.12
解题思路:
首先我们弄清一个概念,圆上的一条弦把圆分割成两部分,所得的两部分都称为弓形,因它们的形状似弓而得名。
弓形是一个非正式用语。如没有特别指明,弓形通常指的是加上弦后面积不包含圆心的那一部分。面积比较大的部分称为优弓形,而另一部分则称为劣弓形
所以弓形一定在某个圆上,我们就可以借用某些圆的性质了
这可比我一开始想用微积分简单的多了
这道题问的就是我们要知道 h 是多少,而 h 可以表示为 h=r-r*cos(x) 利用这个式子,我们要探求的是否存在单调性, 如果有就可以直接用二分算法就可以了。
我们可以想到二分角度,因为题目说最大不超过 1.5 倍,所以不用担心单调性。
代码示例:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define PI (acos(-1))
#define eps (1e-14)
double L,n,C,L2,s,L1;
double ans;// 题目要求答案,即 h
double rad;// 半径
int main(){
cin>>L>>n>>C;
if(n*C*L<=eps){//如果太小直接输出就可以了
cout<<"0.000\n";
return 0;
}
L1=(1+n*C)*L;//加热膨胀后的长度
double l,r,mid;//二分的左右边界和中间值
l=0;
r=PI;
while(l<r-eps){//在这个范围内时二分就可以了,精度问题
mid=(l+r)/2;
rad=L/2/sin(mid/2);
L2=mid*rad;//L2 表示的是用 mid 求出来的弧长
if(L2>=L1) r=mid;
else l=mid;
}
rad=L/2/sin(l/2);
ans=rad-L/2/tan(l/2);
printf("%.3lf\n",ans);
return 0;
}
还有更优质的题解可以去参考一下:
POJ 1905 Expanding Rods 木棍膨胀_在思索中前行!-CSDN博客