[洛谷1337] 吊打XXX/平衡点 (模拟退火)
题意
n个重物(x,y,w),求平衡时x的位置(x,y)
分析
模拟退火基础题,基于随机数的优化算法,时间复杂度玄学,参数玄学,能不能AC看脸,当然如果参数设定的好,并且在规定时间内多跑几次退火算法,并且每次退火时以上一次退火得到的最优解作为新一次退火的起点,AC的概率还是很大的。
ACcode
#include <bits/stdc++.h>
#define fre freopen("data.in","r",stdin);
#define frew freopen("my.out","w",stdout);
using namespace std;
int n;
struct node{
int x,y,w;
}s[2005];
double ansx,ansy,answ;
double sx,sy,sw;
double energy(double x,double y){
double ans=0,dx,dy;
for(int i=0;i<n;i++){
dx=x-s[i].x;
dy=y-s[i].y;
ans+=(sqrt(dx*dx+dy*dy)*s[i].w);
}
return ans;
}
void sa(){
/*
*影响时间复杂度的主要是降温系数,初始温度和截止温度影响不大,
*降温系数相差一个数量级,时间增加一个数量级
*/
double t=3000;
double down=0.996;
sx=ansx,sy=ansy;//以当前获取的最优解作为退火的起点
while(t>1e-15){//当前温度大于设定的停止退火温度,则继续执行退火操作
//在当前最优解的周围随机出一个新的解
double ex=sx+((rand()<<1)-RAND_MAX)*t;
double ey=sy+((rand()<<1)-RAND_MAX)*t;
double ew=energy(ex,ey);
double dif=ew-answ;
if(dif<0){//新解比最优解要优,更新最优解
ansx=ex,ansy=ey,answ=ew;
sx=ex,sy=ey;
}
else if(exp(-dif/t)*RAND_MAX>rand()){
sx=ex;
sy=ey;
}
t*=down;
}
}
double MIN_TIME=0.8;
void solve(){
while((double)clock()/CLOCKS_PER_SEC<MIN_TIME){//在可接受的时间内多执行几次退火
//cout<<clock()<<endl;
sa();
//cout<<ansx<<ansy<<endl;
}
}
int main(){
//fre;frew;
cin>>n;
for(int i=0;i<n;i++){
cin>>s[i].x>>s[i].y>>s[i].w;
ansx+=s[i].x;
ansy+=s[i].y;
}
//初始化解空间为质心
ansx/=n,ansy/=n;
answ=energy(ansx,ansy);
solve();
printf("%.3lf %.3lf",ansx,ansy);
return 0;
}