P1337 [JSOI2004]平衡點 / 吊打XXX 【模擬退火】
阿新 • • 發佈:2021-08-06
洛谷 P1337 -> Click Here
題意
有 \(n\) 個點在一個平面上,且第 \(i\) 個點對點 \(X\) 施一拉力 \(w_i\) ,尋找 \(X\) 的平衡位置
思路
模擬退火經典入門題目,雖然是單峰函式,但也可用退火解決
設初始點為每個點的平衡位置,進行模擬退火,尋找新點,檢視當前位置是否更優或是否在一定可接受的概率內,逐漸降溫,平衡點也逐漸精確
code
#include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #define down 0.996 #define REP(i,a,b) for(int i=(a);i<=(b);i++) using namespace std; struct edge{double x,y,w;} E[100005]; int n; double ansx,ansy,answ; double getde(double x,double y){ double r=0,dx,dy; REP(i,1,n){ dx=x-E[i].x; dy=y-E[i].y; r+=sqrt(dx*dx+dy*dy)*E[i].w; } return r; } void sa(){ double t=4000; while(t>1e-15){ double ex=ansx+(rand()*2-RAND_MAX)*t; double ey=ansy+(rand()*2-RAND_MAX)*t; double ew=getde(ex,ey); double dw=ew-answ; if(dw<0){ ansx=ex; ansy=ey; answ=ew; }else if(exp(-dw/t)*RAND_MAX>rand()){ ansx=ex; ansy=ey; } t*=down; } } int main(){ scanf("%d",&n); REP(i,1,n){ scanf("%lf%lf%lf",&E[i].x,&E[i].y,&E[i].w); ansx+=E[i].x; ansy+=E[i].y; } ansx/=n; ansy/=n; answ=getde(ansx,ansy); int cnt=4;while(cnt--) sa(); printf("%.3lf %.3lf\n",ansx,ansy); return 0; }