[模拟退火]BZOJ 3680 吊打XXX
Description
gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty。gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了。蒟蒻们将
n个gty吊在n根绳子上,每根绳子穿过天台的一个洞。这n根绳子有一个公共的绳结x。吊好gty后蒟蒻们发现由于每个gty重力不同,绳
结x在移动。蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助。
不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上。
Input
输入第一行为一个正整数n(1<=n<=10000),表示gty的数目。
接下来n行,每行三个整数xi,yi,wi,表示第i个gty的横坐标,纵坐标和重力。
对于20%的数据,gty排列成一条直线。
对于50%的数据,1<=n<=1000。
对于100%的数据,1<=n<=10000,-100000<=xi,yi<=100000
Output
输出1行两个浮点数(保留到小数点后3位),表示最终x的横、纵坐标。
Sample Input
3
0 0 1
0 2 1
1 1 1
0 0 1
0 2 1
1 1 1
Sample Output
0.577 1.000
分析
这题luogu居然没有SPJ就跑去BZOJ做咯
像这种优(sha)秀(bi)的题目,又是小数又是SPJ的,我觉得模拟退火这种神(sha)奇(diao)的东西能够胜任这个任务
模拟退火是什么应该不用解释的,不过讲一下退火算法转移需要满足的条件:
Pi→j(状态)需要以下两个要求满足其一即可:
1、j本身比i就优(W(j)<W(i)什么的)
2、退火算法神奇的地方:e(W(i)-W(j))/Temparetrue(仅当W(j)≥W(i)即不是更优解)
第二条要求可能有点晕,不过这就是模拟退火的精髓:当下一解不优时,以这样一个低温函数的概率接受这个解继续求解,这样就有一定的概率跳出局部最优解,得到全局最优解。
对于退火时的初始温度要仔细把控,过小会难以跳出局部最优解,过大时间承受不住
还可以在算法结束后用残余的低温进行随机微调,或许可以得到更优一点的解
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; #define rand (double)rand()/RAND_MAX const double eps=1e-4; const double k=98*1e-2; const int N=10001; struct Point { double x,y,G; }a[N],goal; int n; double temp=1e6,mn=1e18; double Update(Point p) { double ret=0; for (int i=0;i<n;i++) ret+=sqrt(pow(a[i].x-p.x,2)+pow(a[i].y-p.y,2))*a[i].G; if (ret<mn) mn=ret,goal=p; return ret; } void An() { Point now=goal; while (temp>eps) { Point nw; nw.x=now.x+temp*(rand*2-1); nw.y=now.y+temp*(rand*2-1); double dE=Update(now)-Update(nw); if (dE>0||rand<=exp(dE/temp)) now=nw; temp*=k; } for (int i=1;i<=1000;i++) { now.x=goal.x+temp*(rand*2-1); now.y=goal.y+temp*(rand*2-1); Update(now); } } int main() { scanf("%d",&n); for (int i=0;i<n;i++) { scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].G); goal.x+=a[i].x;goal.y+=a[i].y; } goal.x/=n; goal.y/=n; An(); printf("%.3lf %.3lf",goal.x,goal.y); }
在日渐沉没的世界里,我发现了你。