題目大意:
平面上有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軸相切,即,根據圓的公式:
其中爲任意一點座標。這樣就能求出以爲圓心的圓要想包含第i個點的最小半徑(即第i個點在圓周上)。遍歷一遍所有點,即可求出圓心x軸值已知的情況下,圓要想包含所有點的最小半徑。
如果r對x求導,得:
由於是同號的,所以是單調的。令,得,因此在時, 單調遞減,時,單調遞增。先增後減,必有最小值,並且呈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;
}