题目描述
探险队员们跟随两位护法来到了七色虹前。七色虹,就是平面直角坐标系中赤橙黄绿青蓝紫七个半圆,第 i座(1<=i<=7)半圆形彩虹的圆心是(xi,0),半径是ri,半圆上所有点的纵坐标均为非负数。探险队员可以看做一条竖直的、长度等于身高的线段,线段的底端纵坐标为0,最高的一位探险队员的身高为h。
现在探险队员们要从(0,0)穿越七色虹到达(x0,0),穿越七色虹的过程中,探险队员的整个身体必须始终在至少一个半圆形彩虹的内部。由于彩虹的半径 ri 可能太小了,不足以满足这个条件,因此两位护法决定帮助他们把所有彩虹的半径都增大一个非负实数 r。探险队员们想知道,r最小是多少呢?
分析
明显的二分。先按照圆心x坐标排序,方便枚举。若当前二分得到的答案为mid,在check函数里枚举每个坐标。容易知道,若坐标(x,h)在圆内或圆上,则由(x,0)(x,h)组成的线段一定在圆内,所以只需考虑点(x,h)即可。枚举从0到x0,若在一个圆内,则将当前i移到圆的边上,再跳到下一个圆,进行同样的判定。最后看当前指针有无超过7,超过则返回false,否则返回true。
代码
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pw(x) ((x)*(x))
using namespace std;
const double eps=1e-5;
struct Circle {
double x,ro;
}a[10];
double h,x0;
double l,r,mid;
double dis(double x1,double y1,double x2,double y2) {
return sqrt(pw(x1-x2)+pw(y1-y2));
}
double calc(int x,double mid) {
//x=+sqrt(r^2-(y-b)^2)+a;//用圆的方程(x-a)^2+(y-b)^2=r^2变形舍去负根
return sqrt(pw(a[x].ro+mid)-pw(h))+a[x].x;
}
bool check(double mid) {
int now=0;
for (double i=0;i<=x0&&now<=7;) {
now++;
if (dis(i,h,a[now].x,0)<=a[now].ro+mid) {
i=calc(now,mid);
}
}
if (now<=7) return true;
else return false;
}
bool cmp(Circle a,Circle b) {
return a.x<b.x;
}
int main() {
// freopen("rainbow.in","r",stdin);
// freopen("rainbow.out","w",stdout);
scanf("%lf%lf",&h,&x0);
for (int i=1;i<=7;i++)
scanf("%lf%lf",&a[i].x,&a[i].ro);
sort(a+1,a+7+1,cmp);
l=0,r=100000000;
while (l+eps<r) {
mid=(l+r)/2;
if (check(mid)) r=mid;
else l=mid;
}
printf("%.2lf",l);
return 0;
}