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=ry=r,根據圓的公式:
r2=(xxi)2+(ryi)2r=(xxi)2+yi22yir^2=(x-x_i)^2+(r-y_i)^2\rightarrow r=\frac{(x-x_i)^2+y_i^2}{2y_i}
其中(xi,yi)(x_i,y_i)爲任意一點座標。這樣就能求出以(x,r)(x,r)爲圓心的圓要想包含第i個點的最小半徑(即第i個點在圓周上)。遍歷一遍所有點,即可求出圓心x軸值已知的情況下,圓要想包含所有點的最小半徑。
如果r對x求導,得:
rx=4yix+4xiyi4yi2r_x=\frac{4y_ix+4x_iy_i}{4y_i^2}
rxx=16yi316yi4=1yir_{xx}=\frac{16y_i^3}{16y_i^4}=\frac{1}{y_i}
由於yiy_i是同號的,所以rxr_x是單調的。令rx=0r_x=0,得x=xix=-x_i,因此在x<xix<-x_i時, rr單調遞減,x>xix>-x_i時,rr單調遞增。rr先增後減,必有最小值,並且呈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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章