[JSOI2004]平衡点 / 吊打XXX 题解

预备概念:

  金属退火:将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却

  温度:一个逐渐减小的参数,表示接受次优解的概率

模拟退火是一种解决复杂问题的算法,相当于贪心,但以一个逐渐减小的该率接受次优解,当然,该次优解距最优解越远,同等温度下被接受的概率也就越小

参考洛谷日报上某巨佬代码(主要是参考在何时接受次优解,生成正负随机数的技巧以及如何确定退火初值),打了一下洛咕P1337

接受次优解:

  exp (  ( ans            -         cur  )    /   temp)   *        RAND_MAX             >      rand()

       当前最优解              当前解    温度             定值,参考cstdlib                 随机数

调参失败时具体技巧:

  1:开始的随机种子一定要多随机几次(玄学???)

  2:调节温度下降的速度(至少0.99以上QWQ)

  3:合理调整最终答案的处值,模拟退火算法可以多执行几次,判定算法结束的条件精度高

  4:用一些玄学数字:19******

#include<cstdio>
#include<cstdlib>
#include<cmath>
#define ll long long
#define ri register int

const int MAXN = 1005;
const int SA_TIME = 4;
const double DELTA = 1e-14;

ll read(){
    ll x = 0; int zf = 1; char ch = ' ';
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}

int x[MAXN], y[MAXN], w[MAXN];
int n;

double ansx = 0, ansy = 0, ans=1e18;
const double delta = 0.993;

inline double getDis(double x1, double y1, double x2, double y2){
    return (sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
}

inline double cacEny(double curx,double cury){
    double sum = 0;
    for (ri i = 1; i <= n; ++i)
        sum += getDis(x[i], y[i], curx, cury) * w[i];
    return sum;
}

inline int getRand(){
    //需要返回负数,故特殊处理rand函数
    return ((rand() << 1) - RAND_MAX);
}

inline void SA(){
    double sx = ansx, sy = ansy, curx, cury;
    double temp = 1926;
    while (temp >= DELTA){
        if (temp < 10)
            temp = temp - 1 + 1;
        curx = sx + getRand() * temp, cury = sy + getRand() * temp;
        double cur_eny = cacEny(curx, cury);
        if (cur_eny < ans){
            sx = curx, sy = cury;
            ansx = curx, ansy = cury, ans = cur_eny;
        }//接受次优解 
        else {
            if (exp((ans - cur_eny) / temp) * RAND_MAX > rand())
                sx = curx, sy = cury;
        }
        temp *= delta;
    }
}

inline void Solve() {
    int sumx = 0, sumy = 0;
    for (ri i = 1; i <= n; ++i)
        sumx += x[i], sumy += y[i];
    ansx=(double)(sumx) / n, ansy=(double)(sumy) / n;
    for (ri i = 1; i <= SA_TIME; ++i)
        SA();
}

int main() {
    srand(71806291); srand(rand()); srand(rand()); srand(rand());
    n = read();
    for (ri i = 1; i <= n; ++i)
        x[i] = read(), y[i] = read(), w[i] = read();
    Solve();
    printf("%.3f %.3f\n", ansx, ansy);
    return 0;
}

未经博主允许禁止转载

参考资料:
  洛谷日报2018年9月#60 地址 作者:M_sea

  exp函数 地址 作者:百度百科

posted @ 2019-03-31 00:25  LinZhengmin  阅读(254)  评论(0编辑  收藏  举报

Contact with me