题目:https://ac.nowcoder.com/acm/contest/3006/B
参考题解:https://ac.nowcoder.com/acm/problem/blogs/201956
三分法参考博客:https://blog.csdn.net/beiyouyu/article/details/7875480?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
这个题目的方法就是对答案三分,接下来详细说一下三分法
我们都知道二分可以求具有单调性的函数中的某个值。
三分法主要用于解决寻找单峰函数的极值,而这个题目我们分析一下范围可以大致得出随着x增大,最大距离是先增后减的。
所以三分的具体方法就是我们要将分为两半,然后在对其中的一变进行再分两半,总体就是2:1:1的形式。这里,我们用m表示一半,用mm表示一半的一半。要求最小值就只需要每次将边界移到大的那一边
那么还有一个问题,查找答案应该循环多少次?如何将它表示出来?
有两种方法:1.直接用for循环一定的次数。
这里用的是100次。因为每一次循环,都将剩余的区间进行了又一次的三分,这都是2的负次幂级的下降。所以,2的-100次幂再乘以区间长度的值应该是足够精确的了。
2.while循环
while (l + EPS < r)
这里的EPS是一个无限小的数,这个很好理解就不再多说。
代码如下:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f;
const int N=100005;
using namespace std;
typedef long long ll;
struct node{
int x,y;
}a[N];
int n;
double maxn;
double check(double c){
maxn=0.0;
for(int i=1;i<=n;i++){
double sum=a[i].y*a[i].y+(a[i].x-c)*(a[i].x-c);
maxn=max(maxn,sum);
}
return maxn;
}
void slove(double l,double r){
for(int i=1;i<=100;i++){
double m=l+(r-l)/2;
double mm=l+(m-l)/2;
if(check(mm)>check(m))
l=mm;
else
r=m;
}
}
int main ()
{
//freopen("D:\\input.txt", "r", stdin);
//freopen("D:\\output.txt", "w", stdout);
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d %d",&a[i].x,&a[i].y);
slove(-10000,10000);
double ans=sqrt(maxn);
cout<<ans<<endl;
return 0;
}