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;
}
posted @ 2022-03-29 23:35  zyy2001  阅读(102)  评论(0编辑  收藏  举报