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