题目描述
给定一些点,求最小的可以把所有点都覆盖个的圆的半径
思路
什么乱七八糟的计算几何解析几何?都不会……
但我们发现了一些有意思的事情……
- 精确到小数点后6位
- 最多只有50个点
- 坐标最大1000
于是我们想到枚举圆心坐标
当然这明显是不行的
所以我们来用模拟退火
这题退火可跑的飞快,因为只有50个点,枚举一遍非常快
解释见注释
代码
#include<bits/stdc++.h> #define double long double//强行提高精度 using namespace std; struct node{ double x,y; }a[105]; int n; double ansx,ansy,sy=0,sx=0; double ans=2000,t; double del=0.9995; //这个系数不算低,但是稳 inline double dis(node aa,node bb){ return sqrt((aa.x-bb.x)*(aa.x-bb.x)+(aa.y-bb.y)*(aa.y-bb.y)); }//求欧几里得距离,用来看这个点里目前的圆心多远 double calc(double x,double y){ double ans=0; for(int i=1;i<=n;i++){ ans=max(ans,dis((node){x,y},a[i])); } return ans; }//计算出若圆心在此处需多大半径 void SA(){//模拟退火 double x=ansx,y=ansy; t=2000;//初温2000 while(t>1e-14){//精细的降温 double X=x+((rand()<<1)-RAND_MAX)*t;//随机得到一个x double Y=y+((rand()<<1)-RAND_MAX)*t;//随机得到一个y double now=calc(X,Y); double Delta=now-ans;//查看差值 if(Delta<0){//接受这个新解 x=X,y=Y; ans=now; } else if (exp(-Delta/t)*RAND_MAX>rand()) x=X,y=Y; //以一定概率接受这个新解 t*=del; } } int main(){ int cnt=0; srand(1000211237);//设置种子 scanf("%d",&n); for(int i=1;i<=n;i++){ cin>>a[i].x>>a[i].y; sx+=a[i].x,sy+=a[i].y; } ansx=(double)sx/n,ansy=(double)sy/n; while((double)clock()/CLOCKS_PER_SEC<0.93) SA(),cnt++; //压线多次退火 printf("%.14Lf",ans);//输出最后答案 return 0; }
最后一次更新于2020-02-26
0 条评论