1.穿越七色虹3808
(cross.cpp/c/pas)
【问题描述】
探险队员们跟随两位护法来到了七色虹前。七色虹,就是平面直角坐标系中赤橙黄绿青蓝紫七个半圆,第 i座(1<=i<=7)半圆形彩虹的圆心是(xi,0),半径是ri,半圆上所有点的纵坐标均为非负数。探险队员可以看做一条竖直的、长度等于身高的线段,线段的底端纵坐标为0,最高的一位探险队员的身高为h。
现在探险队员们要从(0,0)穿越七色虹到达(x0,0),穿越七色虹的过程中,探险队员的整个身体必须始终在至少一个半圆形彩虹的内部。由于彩虹的半径 ri 可能太小了,不足以满足这个条件,因此两位护法决定帮助他们把所有彩虹的半径都增大一个非负实数 r。探险队员们想知道,r最小是多少呢?
【输入】
第一行两个实数h、x0,表示身高和目的地横坐标。
接下来七行每行两个实数xi、ri,表示七座半圆形彩虹的圆心和半径。
【输出】
输出最小的r,四舍五入保留 2 位小数。
【输入样例】
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
【输出样例】
1.00
【数据范围】
对于100%的数据,满足0<=xi,x0<=10000,0<h<100。
一道很坑的实数二分..
当然是二分r,用这个r加上原来的ri,得到新的圆的方程,交y=h,可解x,一个圆有1个或2个交点,记为xl,xr。
y=h是“最低需求”,那么可知区间[0,x0]一定被由xl,xr的多条线段覆盖,用这个check即可。
考试的时候实力脑残只判断了x[i].r<x[i+1].l return 0;就完全没想到线段覆盖啊啊啊!
还没事去加了个二次方程delta的判断,对于每一个圆,没有交点就+r加到有交点为止...后来才发现这个是错的,很简单,比如下面这个情况:
这种情况,下面这个圆就可以直接忽略掉..
当然,这个也在某种程度上是必要的,因为sqrt(x),若x为负数,那么结果为-INF,会影响线段覆盖算法,需要判断一下。
当然数据很水直接就这么过了哈哈。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node
{
double o,r;
}a[15];
struct nod
{
double l,r;
}x[100005];
int n=7;
double ed,h,eps=0.000001,xl[105],xr[105];
bool cmp(node x,node y)
{
return x.o<y.o;
}
void init()
{
scanf("%lf%lf",&h,&ed);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].o,&a[i].r);
sort(a+1,a+n+1,cmp);
}
double squ(double x){return x*x;}
bool cp(nod a,nod b)
{
return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
bool check(double delta)
{
for(int i=1;i<=n;i++)x[i].l=-sqrt(squ(a[i].r+delta)-squ(h))+a[i].o;
for(int i=1;i<=n;i++)x[i].r=sqrt(squ(a[i].r+delta)-squ(h))+a[i].o;
sort(x+1,x+n+1,cp);
double l=x[1].l,r=x[1].r;
if(l>0.0)return 0;
for(int i=1;i<n;i++)
{
if(x[i+1].l>=l&&x[i+1].r<=r)continue;
if(x[i+1].l<=r){r=x[i+1].r;continue;}
}
return r>ed||(fabs(r-ed)<eps);
}
void work()
{
double l=0,r=10000000;
double ans;
while((r-l)>eps)
{
double mid=(l+r)/2;
if(check(mid))
r=mid;
else l=mid;
}
printf("%.2lf",l);
}
int main()
{
init();
work();
return 0;
}
有些基础的模型需要记得啊..