题目链接

题意:
n个点,给定(Xi,Yi)的坐标,问:找到一个最小半径的圆,既与x轴相切,又包含所有的n个点。

边界条件:
当n个点,既有出现在x轴上方的,又有出现在x轴下方的点时,不存在。
当n个点,出现了至少两个点在x轴上时,不存在。
画画图就能想明白。
圆需要与x轴相切,必然与x轴只有1个交点。

二分做法:
图片说明
给定r,判断是否可行。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 20;
int n;
double x[maxn];
double y[maxn];

int ok(double r){
    double xleft = -1e15, xright = 1e15, tmp;
    for(int i = 1; i <= n; i++){
        if (y[i] > 2 * r) return 0;
        tmp = sqrt(2 * r * y[i] - y[i] * y[i]);
        xleft = max(xleft, x[i] - tmp);
        xright = min(xright, x[i] + tmp);
    }
    return xleft <= xright;
}

int main(){
    int xcnt = 0;
    int yup = 0;
    int ydown = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%lf%lf", &x[i], &y[i]);
        if (y[i] == 0) xcnt ++;
        if (y[i] > 0) yup = 1;
        if (y[i] < 0) ydown = 1, y[i] = - y[i];
    }
    if ((yup && ydown) || (xcnt >= 2))
        printf("-1\n");
    //if (yup && ydown) printf("-1\n");
    else{
        int cnt = 100;
        double l = 0, r = 1e15, mid, ans;
        while(cnt --){
            mid = (l + r) / 2.0;
            if (ok(mid)){
                ans = mid;
                r = mid;
            }
            else l = mid;
        }
        printf("%.6lf\n", ans);
    }
    return 0;
}

着重想讲一讲三分
二分:处理的是单调性问题。能够找到边界值。最大,最小。在[L,R]区间内,函数是单调的。有些值,ok;有些值不ok。
三分:可以处理的是带有一个拐点的问题。能够找到极值。极大或极小。在[L,R]区间内,函数有极值。在极值两侧,单调性相反。
本题中,假设圆心坐标为(x,r),即有:
(x - xi) ^ 2 + (r - yi) ^ 2 = r ^ 2
得到:r = ((x - xi) ^ 2 + yi ^ 2) / (2 * yi)
那么有R = max(r1, r2, ..., rn)
考虑x往-inf,和inf两侧,即x正负半轴走,R都是变大的。
即存在一个极值X0,有左边递减,右边递增,故可以三分。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 20;
int n;
double x[maxn];
double y[maxn];

double calc(double xx){
    double r = 0;
    for(int i = 1; i <= n; i++)
        r = max(r, ((xx - x[i]) * (xx - x[i]) + y[i] * y[i]) / (2.0 * y[i]));
    return r;
}

int main(){
    int xcnt = 0;
    int yup = 0;
    int ydown = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%lf%lf", &x[i], &y[i]);
        if (y[i] == 0) xcnt ++;
        if (y[i] > 0) yup = 1;
        if (y[i] < 0) ydown = 1, y[i] = - y[i];
    }
    if ((yup && ydown) || (xcnt >= 2))
        printf("-1\n");
    //if (yup && ydown) printf("-1\n");
    else{
        int cnt = 100;
        double l = -1e7, r = 1e7, dx, ml, mr;
        while(cnt --){
            dx = (r - l) / 3.0;
            ml = l + dx;
            mr = r - dx;
            if (calc(ml) < calc(mr)) r = mr;
            else l = ml;
        }
        printf("%.6lf\n", calc(r));
    }
    return 0;
}