Nature Reserve(三分)

CodeForces - 1059D

题目大意

平面上有n个点,现在需要找这么一个圆,它与y=0相切,且这n个点都在圆上或圆内,求这个圆的最小半径Input输入n,1 <= n <= 10^5
接下来n行,每行表示一个点的坐标,依次是x,y,坐标都是整数且绝对值不超过107,同时不存在y=0的点Output这样的圆如果不存在,输出-1,存在则输出其最小半径,误差小于10-6视为正确,即你的输出可以与样例不同,只要误差在范围内即可
Examples
Input
1
0 1
Output
0.5
Input
3
0 1
0 2
0 -3
Output
-1
Input
2
0 1
1 1
Output
0.625
NoteIn the first sample it is optimal to build the circle with the radius equal to 0.5 and the center in (0, 0.5).

思路
由于圆和x横坐标相切,因此所有点必须在x轴得一侧,否则圆势必穿过x轴。
已只圆于x轴相切,即 y = r y=r ,根据圆的公式:
r 2 = ( x x i ) 2 + ( r y i ) 2 r = ( x x i ) 2 + y i 2 2 y i r^2=(x-x_i)^2+(r-y_i)^2\rightarrow r=\frac{(x-x_i)^2+y_i^2}{2y_i}
其中 ( x i , y i ) (x_i,y_i) 为任意一点坐标。这样就能求出以 ( x , r ) (x,r) 为圆心的圆要想包含第i个点的最小半径(即第i个点在圆周上)。遍历一遍所有点,即可求出圆心x轴值已知的情况下,圆要想包含所有点的最小半径。
如果r对x求导,得:
r x = 4 y i x + 4 x i y i 4 y i 2 r_x=\frac{4y_ix+4x_iy_i}{4y_i^2}
r x x = 16 y i 3 16 y i 4 = 1 y i r_{xx}=\frac{16y_i^3}{16y_i^4}=\frac{1}{y_i}
由于 y i y_i 是同号的,所以 r x r_x 是单调的。令 r x = 0 r_x=0 ,得 x = x i x=-x_i ,因此在 x < x i x<-x_i 时, r r 单调递减, x > x i x>-x_i 时, r r 单调递增。 r r 先增后减,必有最小值,并且呈U形,那么我们可以取Left为-inf,Right为+inf,将区间不断逼近谷底即可。

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const double eps = 1e-8;
int n;
double getRidux(long double x) {
	long double r = 0;
	for (int i = 0; i < n; i++){
		r = max(r, (x - points[i].x) * (x - points[i].x) / points[i].y / 2 + points[i].y / 2);
	}
	return r;
}

int main(){

	cin >> n;
	int hasNeg = 0;

	for (int i = 0; i < n; i++){
		scanf("%lf%lf", &points[i].x, &points[i].y);
		if (points[i].y > 0) {
			hasNeg |= 1;
		}
		if (points[i].y < 0) {
			hasNeg |= 2;
		}
		points[i].y = fabs(points[i].y);
	}

	if (hasNeg == 3) {
		puts("-1");
		return 0;
	}

	long double 
		Left = -1e7,
		Right = 1e7, 
		LeftX,
		RightX, 
		DividSector;

	while (Right - Left > eps){
	        //除的数只要大于2就行,越接近2越快,不过可能注意精度问题。
	        //三分法一般除3,但实际上除2.x会更快
	        //等于2得话LeftX和RightX就相等了,那就不是区间逼近了,第一次就区间变点了。。。
		DividSector = (Right - Left) / 2.000001;
		LeftX = Left + DividSector;
		RightX = Right - DividSector;
		if (getRidux(LeftX) - getRidux(RightX) < 0) {
			Right = RightX;
		}
		else {
			Left = LeftX;
		}
	}
	printf("%lf\n", getRidux(Right));
	return 0;
}
发布了71 篇原创文章 · 获赞 80 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42971794/article/details/104606326
今日推荐