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

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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值