星星还是树(模拟退火)
题意
在二维平面上,有\(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;
}