一道入門模擬退火的經典題,仍是很考驗RP的git
首先咱們發現神TM這道題又和物理扯上了關係,實際上是一道求廣義費馬點的題目spa
首先咱們能夠根據物理知識獲得,當系統處於平衡狀態時,系統的總能量最小code
又此時系統的總能量是等於各個物體的重力勢能,在質量必定時,即要求物體離地最近,離桌子最遠。blog
那麼,也就是繩子在桌子上的距離儘可能的小,即要求\(\sum_{i=1}^n m_i\cdot dist_{i,x}\)最小get
(以上物理部分推導摘於pym‘s blog)it
而後考慮退火,咱們先選取一個初始位置(通常取全部點座標的平均數方便收斂)io
而後每次退火時給座標隨機一個增量(要隨溫度下降而減小,並注意須要取負的),並計算新的解的答案入門
同時按照通常的模擬退火流程考慮是否接受解而且降溫便可class
PS:本題極大的考驗調參能力,本人Luogu因爲機子快,交了幾發就A了。TM的BZOJ老爺機一直在WA和TLE直接徘徊,最後好像9900+MSA了(時限10S)。im
CODE
#include<cstdio> #include<cstdlib> #include<cctype> #include<cmath> #include<ctime> using namespace std; typedef double DB; const int N=1005; const DB EPS=1e-30,dlt=0.981; struct data { int x,y,w; }a[N]; int n; DB ans_x,ans_y,ave_x,ave_y,ans; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag; } inline DB calc(DB x,DB y) { register int i; DB tot=0; for (i=1;i<=n;++i) tot+=(DB)sqrt((x-a[i].x)*(x-a[i].x)+(y-a[i].y)*(y-a[i].y))*a[i].w; return tot; } inline void Simulate_Anneal(DB x,DB y) { DB T=500,res=calc(x,y); for (;T>EPS;T*=dlt) { DB xx=x+(rand()*2-RAND_MAX)*T,yy=y+(rand()*2-RAND_MAX)*T,now=calc(xx,yy); if (now<ans) ans=now,ans_x=xx,ans_y=yy; if (now<res||exp((res-now)/T)>(DB)rand()/RAND_MAX) res=now,x=xx,y=yy; } } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i,t=50; srand(time(0)); read(n); for (i=1;i<=n;++i) { read(a[i].x); read(a[i].y); read(a[i].w); ave_x+=a[i].x; ave_y+=a[i].y; } ans_x=ave_x=(DB)ave_x/n; ans_y=ave_y=(DB)ave_y/n; ans=calc(ans_x,ans_y); while (t--) Simulate_Anneal(ave_x,ave_y); return printf("%.3lf %.3lf",ans_x,ans_y),0; }