「NOIP2018模拟赛」 穿越七色虹 - 二分

题目描述

探险队员们跟随两位护法来到了七色虹前。七色虹,就是平面直角坐标系中赤橙黄绿青蓝紫七个半圆,第 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值