题目链接:http://poj.org/problem?id=2069
题目大意:给
n
个点的坐标(
再一次见识到了模拟退火的威力。首先我们乱定一个圆心,然后退火乱搞就行了,过程比较简单。注意每次移动的变化量参数
delta
最好定为
0.98
,具体为什么不清楚,但是据网上说设成
0.95
精度就会出现问题。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <time.h>
#include <cmath>
#define EPS (1e-8)
using namespace std;
struct Point
{
double x,y,z;
}points[55];
int n,q; //需要被覆盖的点的个数为n,
double dist(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
double getR(Point t) //求圆心为t的最小覆盖球的半径
{
double tmp=0;
for(int i=1;i<=n;i++)
tmp=max(tmp,dist(t,points[i]));
return tmp;
}
double SA(Point start) //模拟退火,start=最开始确定的圆心
{
double ans=1e20; //答案=最小覆盖球的半径
double delta=100; //每次移动的变化量
while(delta>EPS)
{
int d=1; //d为离当前确定的圆心最远的点的编号
for(int i=1;i<=n;i++)
if(dist(start,points[i])>dist(start,points[d]))
d=i;
double nowr=dist(start,points[d]); //nowr=当前固定圆心的最小覆盖球的半径大小
ans=min(ans,nowr);
start.x+=(points[d].x-start.x)/nowr*delta;
start.y+=(points[d].y-start.y)/nowr*delta;
start.z+=(points[d].z-start.z)/nowr*delta;
delta*=0.98;
}
return ans;
}
int main()
{
srand(time(0));
while(scanf("%d",&n)!=EOF&&n)
{
Point center;
center.x=center.y=center.z=0;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&points[i].x,&points[i].y,&points[i].z);
center.x+=points[i].x;
center.y+=points[i].y;
center.z+=points[i].z;
}
center.x=0;
center.y=0;
center.z=0;
/*
center.x/=n;
center.y/=n;
center.z/=n;*/
printf("%.5lf\n",SA(center));
}
return 0;
}