洛谷P1337 【[JSOI2004]平衡點 / 吊打XXX】(模擬退火)
阿新 • • 發佈:2018-04-21
最小 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】(模擬退火)