# [洛谷1337] 吊打XXX/平衡点 (模拟退火)

[洛谷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;
}
posted @ 2019-09-17 09:35  yhsmer  阅读(182)  评论(0编辑  收藏  举报