您的位置:首页 > 其它

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

2015-02-11 21:54 411 查看
题目链接:http://poj.org/problem?id=2069

题目大意:给nn个点的坐标(xi,yi,zix_i,y_i,z_i),求覆盖这nn个点的最小球的半径rr。

再一次见识到了模拟退火的威力。首先我们乱定一个圆心,然后退火乱搞就行了,过程比较简单。注意每次移动的变化量参数deltadelta最好定为0.980.98,具体为什么不清楚,但是据网上说设成0.950.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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: