您的位置:首页 > 其它

[BZOJ 3680]吊打XXX(广义费马点、模拟退火搜索)

2015-02-02 07:41 405 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=3680

题目大意:求n个带质量的质点的重心,重心定义为与每个点的距离*点的质量之和最小的点。

此题是ICPC Camp第一场Finals模拟赛中yyf神牛的D题解法,就是裸的模拟退火搜索,虽然说精度要求略有些苛刻(1e-3),不过在Finals模拟赛中yyf经过28次调整精度还能实现1e-6的精度,应该问题不大,不过如果代码写得很挫(比如说随机数写挂了),就可能WA或TLE

#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <algorithm>
#include <cmath>

#define MAXN 10100
#define INF 1e17
#define EPS 1e-3
#define PI acos(-1.0)

using namespace std;

double minans=INF; //最小答案(重心到各点距离的加权和)

struct Point
{
double x,y; //点的坐标(x,y)
double weight; //质量
}points[MAXN],centerOfGravity; //centerOfGravity=最终的重心质点

int n; //点的个数

double EuclidDist(Point p1,Point p2) //求点p1到p2的距离
{
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}

double calc(Point x) //用当前假设的重心x更新最小的答案minans,并返回当前假设的重心x得到的最小加权距离和
{
double ans=0; //当前用点x作重心的情况下重心x到各点距离的加权和
for(int i=1;i<=n;i++)
ans+=points[i].weight*EuclidDist(x,points[i]);
if(ans<minans)
{
minans=ans;
centerOfGravity=x;
}
return ans;
}

double Rand()
{
return (rand()%1000)/1000.0;
}

void SA(double T) //模拟退火搜索找n个点的重心,最初的温度是T
{
Point now=centerOfGravity;
while(T>EPS)
{
Point next;
next.x=now.x+T*(Rand()*2-1);
next.y=now.y+T*(Rand()*2-1);
double dE=calc(now)-calc(next); //改变重心位置的能量差dE
if(dE>0||exp(dE/T)>Rand()) now=next; //移动当前的点now
T*=0.993;
}
for(int i=1;i<=1000;i++) //为了使答案更加精确,在最终确定的重心周围再随机地移动一丁点小步,更新答案
{
Point next;
next.x=centerOfGravity.x+T*(Rand()*2-1);
next.y=centerOfGravity.y+T*(Rand()*2-1);
calc(next);
}
}

int main()
{
srand(23333333);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&points[i].x,&points[i].y,&points[i].weight);
centerOfGravity.x+=points[i].x;
centerOfGravity.y+=points[i].y;
}
//大致确定重心的位置
centerOfGravity.x/=n;
centerOfGravity.y/=n;
SA(1000000);
printf("%.3lf %.3lf\n",centerOfGravity.x,centerOfGravity.y);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: