模拟退火



前言

早上大雾课在看数建,花了半节课看完了模拟退火(不要指责窝为什么上课划水,你什么时候看见窝认真听课了(此处为期末翻车埋下了伏笔))
模拟退火总的来说是玄学算法hhhh,用来求一些难以用精确算法求解的问题(NP问题),比如TSP之类的
本篇只有一道题,方便日后搞成模板而已,毕竟近似算法ACM不常用

洛谷P1337 - [JSOI2004]平衡点 _ 吊打XXX


链接
https://www.luogu.org/problemnew/show/P1337

#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
using namespace std;

const int N = 1000 + 15;

struct Point{
    double x, y;
    int w;
};
Point pt[N];
const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};

inline double sqr(double x){
    return x * x;
}

inline double getDis(const Point& a, const Point& b){
    return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}

double getSum(Point p, int n){
    double ret = 0;
    for(int i = 1; i <= n; i++){
        ret += (getDis(p, pt[i]) * pt[i].w);
    }
    return ret;
}

void solve(Point& ansu, int n){
    const double delta = 0.998;
    const double eps = 1e-17;
    double tp = 10000;
    double ans = getSum(ansu, n);
    Point u = ansu;
    while(tp > eps){
        Point v = Point{u.x + (rand()*2-RAND_MAX)*tp, u.y + (rand()*2-RAND_MAX)*tp, 1};
        double tmp = getSum(v, n);
        if(tmp < ans){
            ansu = v;
            u = v;
            ans = tmp;
        }else if(exp(-(tmp - ans)/tp) * RAND_MAX > rand()){
            u = v;
        }
        tp *= delta;
    }
}

int main(){
    srand(time(0));
    int n;
    while(~scanf("%d", &n)){
        for(int i = 1; i <= n; i++){
            scanf("%lf%lf%d", &pt[i].x, &pt[i].y, &pt[i].w);
        }
        Point ansu = pt[1];
        solve(ansu, n);
        solve(ansu, n);
        solve(ansu, n);
        printf("%.3f %.3f\n", ansu.x, ansu.y);
    }
    return 0;
}