由于3的出题人的题解给的较详细,高度难以企及,所以3就没出题解,而4的难度大家都知道,简单的大家都会做,难的我也没找到思路。所以就没写题解了,终于,时隔五六天,终于又开始写题解了,话说,我也好想有个战队啊。
言归正传,题目奉上。
图片说明
根据题目,在一个二维坐标中,我们要输入N个点坐标,而在x轴(-10000,10000)这个区间存在无数个点,我们假设它为x1,x2,x3...这些点中,每个点到我们输入的N个点的距离存在一个最大值,我们要在这些最大值中找到最小的那一个然后输出。
那么,思路是什么呢?
既然存在这样一个点,那么我们就要找到它所在的那个区间。但是,这里要注意了,是最小值,有最值,而且是最小值,那么就可以用三分法那为什么不用二分法呢?因为二分法是对与单调区间的,(这在高中大家接触二分法的时候举得例子就是单调函数吧,我记得好像是单调且端点异号。)而三分法是针对有峰值的函数的。显然,在本题中,用三分法是更加方便快捷的。其实,二分法也可以用,就是对分下来的每一个区间进行再次二分。我觉得要用到递归好像。二分法比较复杂一点。后面我掌握后一定会及时向大家分享的。
下面,我们就来介绍一下三分法的解题步骤:
1.首先,我们要将先分为两半,然后在对其中的一变进行再分两半,总体就是2:1:1的形式。这里,我们用mid表示一半,用midmid表示一半的一半。
这里,我就借鉴一下出题人的代码写法了哈,出题人的代码写得比我的好
2.先来看这些函数:

double check(double x){
    double max=0;
    for(int i=0;i<n;i++){
        double temp=sqrt(a[i].y*a[i].y+(a[i].x-x)*(a[i].x-x));
        if(temp>max)  max=temp;
    }
    return max;
}

很显然,我们就是将在x轴坐标的某个点,到所有的N个点的最大值求出并且返回

double operat(double left,double right){
    int i;
    double mid,midmid;
    for(int i=0;i<100;i++){
        mid=left+(right-left)/2;
        midmid=mid-(mid-left)/2;
        if(check(mid)>check(midmid)){
            right=mid;
        }
        else  left=midmid;
    }
    return mid;
}

我们先搞清楚这个循环,对于这个循环有人可能会问了?为什么是100,其他可不可以,答案是:可以,我们先讲讲这个原理,每一次循环,都将剩余的区间进行了又一次的三分,这都是2的负次幂级的下降。所以,大家可以想想,2的-100次幂再乘以区间长度的值应该是足够精确的了。其实也可以50,30等。但博主试了一下,好像3也是可以过的,emmm,这就要看测试数据本身了,我们为了保险起见,还是将值设大一点。
然后,对于循环语句块,首先,mid我们代表的是区间中点,而midmid有代表的是区间中点的中点。那这里可能又有人要问了,为什么将midmid放在中点左边呢?其实,你要想,也可以放在中点的右边,只是将代码进行一些细微的修改罢了,更有甚者,还可以两边都来个for循环,emmm,只是,没有必要!

for(int i=0;i<100;i++){
        mid=left+(right-left)/2;
        midmid=mid-(mid-left)/2;
        if(check(mid)>check(midmid)){
            right=mid;
        }
        else  left=midmid;
    }

对于这个语句块,如果当前中点(mid)到所有N个点的最大值,大于,当前四分之一分点(midmid)到所有N个点的最大值,而我们的最小值就在一高一低之间,那么最小值就一定在mid左边(注意,我们的midmid在mid的左边)(为什么是左边?这个你可以在纸上画一画),所以将右端点左移到mid,否则,就将左端点移到midmid所在位置。继续进行三分法。如此循环下去。
下面,我就给出总体代码了哈:

#include<bits/stdc++.h>
using namespace std;
struct beg{  //用结构体存储坐标
    int x,y;
}a[100010];
int n;
double check(double x){  //求出当前的点到N个点的最大值
    double max=0;
    for(int i=0;i<n;i++){  //对N个点进行遍历
        double temp=sqrt(a[i].y*a[i].y+(a[i].x-x)*(a[i].x-x));
        if(temp>max)  max=temp;
    }
    return max;
}
double operat(double left,double right){  //进行三分法
    int i;
    double mid,midmid;
    for(int i=0;i<100;i++){  //循环100次,其实可以小一点
        mid=left+(right-left)/2;   //求出区间中点的坐标,注意,是坐标,不是值
        midmid=mid-(mid-left)/2;  //求四分之一区间中点
        if(check(mid)>check(midmid)){  //必将二分之一和四分之一点分别到N个点的最大值
            right=mid;   //右(mid)高左(midmid)低,这将范围缩至左边
        }
        else  left=midmid;  //反之,这区间往右移
    }
    return mid;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i].x>>a[i].y;
    }
    double ans=operat(-10000,10000);   //得到满足条件的最小值的坐标
    cout<<check(ans)<<endl;  //求出其最小值
    return 0;
}

好了,今天的题解就到这里了。哦,对了,今天是情人节吧,虽然不过洋节,但是还是祝福大家在新的一年早日脱单,有情人终成眷属!2020大家平安!有疑问记得下方留言。