BSOJ 3808:穿越七色虹 二分,线段覆盖

29 篇文章 0 订阅
11 篇文章 0 订阅
博客详细介绍了如何使用二分法解决一道关于穿越七色虹的数学问题,其中涉及到线段覆盖和贪心策略。在给定的彩虹半径上增加一个非负实数r,确保探险队员穿越过程中始终在至少一个半圆内。通过计算新圆与线段的交点,判断区间是否被覆盖,最终找到最小的r。
摘要由CSDN通过智能技术生成
1.穿越七色虹3808
(cross.cpp/c/pas)
【问题描述】
  探险队员们跟随两位护法来到了七色虹前。七色虹,就是平面直角坐标系中赤橙黄绿青蓝紫七个半圆,第 i座(1<=i<=7)半圆形彩虹的圆心是(xi,0),半径是ri,半圆上所有点的纵坐标均为非负数。探险队员可以看做一条竖直的、长度等于身高的线段,线段的底端纵坐标为0,最高的一位探险队员的身高为h。

graphic

  现在探险队员们要从(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;
}
有些基础的模型需要记得啊..

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值