1. 程式人生 > >codeforces round514 D Natural reserve(簡單幾何+二分)

codeforces round514 D Natural reserve(簡單幾何+二分)

題意

座標軸中有 n n 個點,需要畫一個圓,這個人與 x x 軸相切,且包含全部的 n

n 個點,問這個圓的半徑最小是多少。

題解

二分搜尋半徑,觀察可以發現,這個圓的圓心一定是在 y = R y = R 上面移動的,所以我們可以依據這個半徑確定每個點的圓心在 y

= R y=R 這條線上的範圍,如果所有的點的圓心的範圍有交集,那麼這個 R R 就是符合條件的。
對於一個點,其可能的圓心範圍是 l
= x R 2 ( y R ) 2 , r = l = x + R 2 ( y R ) 2 l = x-\sqrt{R^2-(y-R)^2}, r =l = x+\sqrt{R^2-(y-R)^2}
,需要判斷一下根號下為負數的情況,還有直接 R R R*R 會爆精度,需要化簡一下。

程式碼

#include <iostream>
#include <cmath>
using namespace std;
const int maxn = 1e5+5;
const double EPS = 1e-8;
int n;
struct Node {
    double x,y;
}cor[maxn];

bool ok(double x) {
    double l = -1e10, r = 1e10;
    for(int i = 0; i < n; ++i) {
        double h = abs(cor[i].y-x);
        if(h > x) return 0;
        double range = sqrt(cor[i].y*(2*x-cor[i].y));
        l = max(l, cor[i].x-range), r = min(r, cor[i].x+range);
    }
    return (r-l)>EPS;
}
int main() {
    scanf("%d", &n);
    bool fg1 = false, fg2 = false;
    for(int i = 0; i < n; ++i) {
        scanf("%lf%lf", &cor[i].x, &cor[i].y);
        if(cor[i].y < 0)
            fg1 = true, cor[i].y = -cor[i].y;
        else 
            fg2 = true;
    }
    if(fg1 && fg2) {
        puts("-1");
    }
    else if(n == 1) {
        printf("%lf\n", cor[0].y/2);
    }
    else {
        double l = 0, r = 1e18, ans;
        for(int i = 0; i < 1000; ++i) {
            double mid = (l+r)/2;
         //   cout << mid << endl;
            if(ok(mid)) {
                r = mid;
                ans = mid;
            }
            else l = mid;
        }
        printf("%lf\n", ans);
    }
    return 0;
}