给出平面上n个有质量的点;
求这n个点的质心位置;
n<=10000;
题解:
这个似乎没什么好的算法啊。。。
但是如果模拟一下这个过程,就会得到一个有点意思的算法——爬山算法;
爬山算法通常被用于计算几何最优化问题的骗分;
与模拟退火不同,它要求答案函数单峰,或者可以说模拟退火是对爬山算法的改进吧;
想象这个绳结被这些点拉着,那么无论初始在哪里,都会到那个质心位置;
那就从任意任意一个点出发,向这个点的合外力方向走一段;
走着走着就到答案点啦!
具体一点的话,每一步走多少呢?
可以暂且设为一个值T;
第一步走T这么长,第二步走短一些,第三步再短...直到步长小于EPS为止;
这样yy一下,似乎最后还是会到质心位置嘛;
注意这里其实是不严格的,显然最终的解受第一步长T和变短的数量有关;
但是如果参数设置合理,最终得到的解就会在误差允许范围内了;
这个算法我不会算复杂度,不过同样和参数有关就是了,精度要求很高时不要使用这种算法;
至于为什么不写模拟退火。。显然单峰问题爬山比较靠谱嘛!
(其实我只是模拟退火写挂了被卡调不出来)
代码:
#include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 11000 using namespace std; const double EPS=1e-10; const double INF=1e100; struct Point { double x,y; Point(){} Point(double _,double __):x(_),y(__){} friend Point operator +(Point a,Point b) { return Point(a.x+b.x,a.y+b.y); } friend Point operator -(Point a,Point b) { return Point(a.x-b.x,a.y-b.y); } friend Point operator *(double a,Point b) { return Point(a*b.x,a*b.y); } friend double operator *(Point a,Point b) { return a.x*b.x+a.y*b.y; } friend double len(Point a) { return sqrt(a*a); } }p[N]; double g[N]; int n; double drand() { return rand()%1000/1000.0; } Point calc(Point a) { Point ret=Point(0,0); for(int i=1;i<=n;i++) { ret=ret+g[i]/len(p[i]-a)*(p[i]-a); } return 1/len(ret)*ret; } Point slove(Point ans,double T) { Point next,dt; while(T>EPS) { dt=T*calc(ans); next=ans+dt; ans=next; T*=0.9; } return ans; } int main() { int m,i,j,k; Point ans=Point(0,0); double ma=0; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lf%lf%lf",&p[i].x,&p[i].y,g+i); ans.x+=p[i].x,ans.y+=p[i].y; ma=max(ma,max(fabs(p[i].x),fabs(p[i].y))); } ans.x/=n,ans.y/=n; ans=slove(ans,ma); printf("%.3lf %.3lf\n",ans.x,ans.y); return 0; }