Description
探险队员们跟随两位护法来到了七色虹前。七色虹,就是平面直角坐标系中赤橙黄绿青蓝紫七个半圆,第i座(1<=i<=7)半圆形彩虹的圆心是(xi,0),半径是ri,半圆上所有点的纵坐标均为非负数。探险队员可以看做一条竖直的、长度等于身高的线段,线段的底端纵坐标为0,最高的一位探险队员的身高为h。
现在探险队员们要从(0,0)到达(x0,0),穿越彩虹的过程中,探险队员的整个身体必须始终在至少一个半圆形彩虹的内部。由于彩虹的半径ri可能太小了,不足以满足这个条件,因此两位护法决定帮助他们把所有彩虹的半径都增大一个非负实数r。探险队员们想知道,r最小是多少呢?
Input
第一行两个实数h、x0,表示身高和目的地横坐标。
接下来七行每行两个实数xi、ri,表示七座半圆形彩虹的圆心和半径。
Output
输出最小的r,四舍五入保留0位小数。
Sample Input
输入样例1:
4.0 36.0
0.0 4.0
6.0 4.0
12.0 4.0
18.0 4.0
24.0 4.0
30.0 4.0
36.0 4.0
输入样例2:
87.20 2955.25
9306.25 379.83
1587.65 881.07
653.83 939.21
3384.39 818.71
8069.61 30.49
306.37 300.35
7695.43 646.25
Sample Output
输出样例1:
1
输出样例2:
53
Hint
对于 100% 满足 0<= xi,x0 <=10000,0<h<100。
【分析】
其实就是二分答案加验证吧,验证的时候用勾股定理加线段覆盖。
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
double h,x0,x[8],r[8],tr[8],tl[8];
void _init()
{
scanf("%lf%lf",&h,&x0);
for(int i=1;i<=7;i++)
scanf("%lf%lf",&x[i],&r[i]);
}
bool _can_match(double Mid)
{
memset(tl,0,sizeof(tl));
memset(tr,0,sizeof(tr));
double ad,R,L;
for(int i=1;i<=7;i++) //找出每段线段
if(r[i]+Mid>h)
{
ad=sqrt((Mid+r[i])*(Mid+r[i])-h*h);
tl[i]=x[i]-ad;
tr[i]=x[i]+ad;
}
R=0;L=x0;
for(int i=1;i<=7;i++) //判断是否覆盖0~x0
if(r[i]+Mid>h)
{
if(tl[i]<=R&&R<=tr[i])
R=tr[i];
if(tl[i]<L)
L=tl[i];
}
if(L<=0&&R>=x0)
return true;
return false;
}
void _solve()
{
for(int i=1;i<=7;i++) //排序
for(int j=i+1;j<=7;j++)
if(x[i]>x[j])
{
swap(x[i],x[j]);
swap(r[i],r[j]);
}
double Left=0,Right=1000000.0,Mid,ans=0; //二分答案
bool mark;
while(Left<=Right)
{
Mid=(Left+Right)/2;
mark=_can_match(Mid);
if(mark)
{
ans=Mid;
Right=Mid-0.0001;
}
else
Left=Mid+0.0001;
}
int temp=0; //四舍五入
ans*=10;
temp=(int)(ans);
if(temp%10>=5)
temp=temp/10+1;
else
temp/=10;
printf("%d\n",temp);
}
int main()
{
_init();
_solve();
return 0;
}