3167. 星星还是树
题目链接
3167. 星星还是树
在二维平面上有 \(n\) 个点,第 \(i\) 个点的坐标为 \((x_i,y_i)\)。
请你找出一个点,使得该点到这 \(n\) 个点的距离之和最小。
该点可以选择在平面中的任意位置,甚至与这 \(n\) 个点的位置重合。
输入格式
第一行包含一个整数 \(n\)。
接下来 \(n\) 行,每行包含两个整数 \(x_i,y_i\),表示其中一个点的位置坐标。
输出格式
输出最小距离和,答案四舍五入取整。
数据范围
\(1≤n≤100,\)
\(0≤x_i,y_i≤10000\)
输入样例:
4
0 0
0 10000
10000 10000
10000 0
输出样例:
28284
解题思路
模拟退火
大概在一段连续的线上面找全局最值的问题,有一个温度(步长)的概念,即每次将答案区间缩小的趋势,以找最小值为例,先随机在答案区间内生成一个点,要保证一开始的温度要覆盖整个答案区间,然后再在当前覆盖的区间内生成一个点,判断新点是否低于当前点,如果是,则当前点肯定不是答案,跳到新点,否则以一定的概率跳到新点,否则如果干脆不跳的话可能最后找到的是局部最小值,且如果新点与当前点越接近,跳的概率越高。温度最好呈指数级别衰减,像0.99
,另外当需要概率跳点时可以 \(e^{-\frac{\delta}{T}}\) 作为概率,可多模拟几次,还可以运行时间作为模拟次数的依据
本题,如果在某处存在一个点满足要求,则其附近的点也大概满足要求,即满足连续性,等价于在一条线上找最小值
- 时间复杂度:随机
代码
// Problem: 星星还是树
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/3170/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
typedef pair<double,double> pdd;
const int N=105;
pdd a[N];
double ans=1e8;
int n;
double rand(double l,double r)
{
return (double)rand()/RAND_MAX*(r-l)+l;
}
double get_dis(pdd x,pdd y)
{
double dx=x.fi-y.fi;
double dy=x.se-y.se;
return sqrt(dx*dx+dy*dy);
}
double cal(pdd x)
{
double res=0;
for(int i=1;i<=n;i++)
res+=get_dis(x,a[i]);
ans=min(ans,res);
return res;
}
void simulate_anneal()
{
pdd cur(rand(0,10000),rand(0,10000));
for(double T=10000;T>=0.004;T*=0.99)
{
pdd np(rand(cur.fi-T,cur.fi+T),rand(cur.se-T,cur.se+T));
double dt=cal(np)-cal(cur);
if(exp(-dt/T)>rand(0,1))cur=np;
}
}
int main()
{
double s=clock();
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].fi,&a[i].se);
while(((double)clock()-s)/CLOCKS_PER_SEC<0.8)simulate_anneal();
printf("%.lf",ans);
return 0;
}