星星还是树(模拟退火)

题意

在二维平面上,有\(n\)个点,找到距离这\(n\)个点距离之和最小的点。

数据范围

\(1 \leq n \leq 100\)
\(0 \leq x_i, y_i \leq 10000\)

思路

模拟退火模板。这道题很容易证明是连续函数,因此可以使用模拟退火。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <ctime>

using namespace std;

typedef pair<double, double> pdd;

const int N = 110;

int n;
pdd q[N];
double ans = 1e8;

double rand(double l, double r)
{
    return (double)rand() / RAND_MAX * (r - l) + l;
}

double get_dist(pdd a, pdd b)
{
    double distx = a.first - b.first;
    double disty = a.second - b.second;
    return sqrt(distx * distx + disty * disty);
}

double calc(pdd x)
{
    double res = 0;
    for(int i = 1; i <= n; i ++) res += get_dist(x, q[i]);
    ans = min(res, ans);
    return res;
}

void simulate_anneal()
{
    pdd cur = {rand(0, 10000), rand(0, 10000)};
    for(double t = 1e4; t > 1e-4; t *= 0.99) {
        pdd np = {rand(cur.first - t, cur.first + t), rand(cur.second - t, cur.second + t)};
        double dt = calc(np) - calc(cur);
        if(exp(-dt / t) > rand(0, 1)) cur = np;
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%lf%lf", &q[i].first, &q[i].second);
    while((double)clock() / CLOCKS_PER_SEC < 0.8) simulate_anneal(); //卡时间的方法
    printf("%.0lf\n", ans);
    return 0;
}
posted @ 2021-01-16 21:53  pbc的成长之路  阅读(122)  评论(0编辑  收藏  举报