[POJ 2069]Super Star(爬山搜索、最小球覆盖)

题目链接:http://poj.org/problem?id=2069
题目大意:给 n 个点的坐标( xi,yi,zi ),求覆盖这 n 个点的最小球的半径 r
再一次见识到了模拟退火的威力。首先我们乱定一个圆心,然后退火乱搞就行了,过程比较简单。注意每次移动的变化量参数 delta 最好定为 0.98 ,具体为什么不清楚,但是据网上说设成 0.95 精度就会出现问题。

#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(随机化,近似化算法,POJ,ACM-ICPC)