1. 程式人生 > >洛谷P1337 【[JSOI2004]平衡點 / 吊打XXX】(模擬退火)

洛谷P1337 【[JSOI2004]平衡點 / 吊打XXX】(模擬退火)

最小 com flash www. show over 接受 http 一個點

洛谷題目傳送門

很可惜,充滿Mo力的Mo擬退火並不是正解。不過這是一道最適合開始入手Mo擬退火的好題。

對模擬退火還不是很清楚的可以看一下

這道題還真和能量有點關系。達到平衡穩態的時候,物體的總能量應該是最小的。而總的能量來源於每個物體的重力勢能之和。要想讓某個物體勢能減小,那就讓拉著它的繩子在桌面下方的長度盡可能的長,也就是桌面上的要盡可能短。由此看來,某個物體的勢能與桌面上的繩子的長度、物體重量都成正比。

於是,為了找到平衡點,我們要找一個點使得\(\sum_{i=1}^n d_i*w_i\)最小(\(d_i\)\(i\)點到該點的距離)。

函數已經求出來了,接下來就是正常的模擬退火過程。註意一些細節就好了。

首先,初始解可以設成\(({\sum_{i=1}^n x_i\over n},{\sum_{i=1}^n y_i\over n})\),可以更接近正解

解變動值最好隨機兩個值,\(\Delta x\)\(\Delta y\)。隨機變動距離和角度可能常數有點大。

然後就是調參數的問題了。蒟蒻太懶,於是就抄了YL據老的,此題參數普遍設大一點是合理的。

最後是寫法問題。蒟蒻又學習了一招,知道有一個常數叫RAND_MAX,用於生成\([0,1)\)的隨機值還是很方便的,在不同機子下可移植性也很強。(難怪Windows上rand出來的都是短整形數)

#include<cstdio>
#include<cmath>
#include<ctime> #include<cstdlib> #define RG register #define R RG double #define RD T*((rand()<<1)-RAND_MAX)//生成一個-T到T的隨機變動距離 const int N=1009; const double D=0.99,EPS=1e-15; int n; double x[N],y[N],w[N]; inline double calc(R x0,R y0){//函數求值 R res=0,dx,dy; for(RG int i=1;i<=n;++i){ dx=x[i]-x0;dy=y[i]-y0; res+=sqrt(dx*dx+dy*dy)*w[i]; } return
res; } int main(){ R T,avx=0,avy=0,start,x0,y0,x1,y1,res,ans,best,bx,by; RG int i,times=10; scanf("%d",&n); for(i=1;i<=n;++i){ scanf("%lf%lf%lf",&x[i],&y[i],&w[i]); avx+=x[i];avy+=y[i]; }//初始橫縱坐標均選擇平均值 best=start=calc(avx/=n,avy/=n);bx=avx;by=avy; srand(time(NULL)*clock()); while(times--){ ans=start;x0=avx;y0=avy; for(T=1000000;T>EPS;T*=D){ x1=x0+RD;y1=y0+RD; res=calc(x1,y1); if(best>res) best=res,bx=x0,by=y0;//更新答案 if(ans>res||exp((res-ans)/T)*RAND_MAX<rand()) ans=res,x0=x1,y0=y1;//接受新解 } } printf("%.3lf %.3lf\n",bx,by); return 0; }

洛谷P1337 【[JSOI2004]平衡點 / 吊打XXX】(模擬退火)