【ICPC】2018南京站 D. Country Meow | 最小球覆盖、模拟退火

122 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

【洛谷】P3425 [POI2005]KOS-Dicing | 二分、网络流

【ICPC】2018南京站 D. Country Meow | 最小球覆盖、模拟退火

题目链接

20182019-acmicpc-asia-nanjing-regional-contest-en.pdf (codeforces.com) Dashboard - 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest - Codeforces

题目

image.png

题目大意

在三维直角坐标系中给出 nn 个点,其中第 ii 个点被描述为 Pi=(xi,yi,zi)P_i=(x_i,y_i,z_i)

找到一个点,最小化它距离给定的所有点的最远距离并输出。

思路

相当于空间中有 11 个半径为 rr 的球使得给定的 nn 个点要么在球内,要么在球面上。输出最小的 rr 是多少。

可以使用模拟退火来求解,具体流程如下:

  1. 初始化温度 T=1000T=1000 和温度下降系数 raterate 和终止条件 epseps,随便取一个点记为中心点 PansP_{ans}
  2. 找到所有点中距离当前中心点距离最远的点 PcurP_{cur},中心点 PansP_{ans}PcurP_{cur} 的距离就是新球的半径。用该半径试图对答案进行更新。
  3. 将中心点向距离中心点最远的点挪动,更新公式如下:
Pans.x=Pans.x+(Pcur.xPans.x)×T1000Pans.y=Pans.y+(Pcur.yPans.y)×T1000Pans.z=Pans.z+(Pcur.zPans.z)×T1000P_{ans}.x=P_{ans}.x+(P_{cur}.x-P_{ans}.x)\times\frac{T}{1000}\\ P_{ans}.y=P_{ans}.y+(P_{cur}.y-P_{ans}.y)\times\frac{T}{1000}\\ P_{ans}.z=P_{ans}.z+(P_{cur}.z-P_{ans}.z)\times\frac{T}{1000}
  1. 更新温度,T=T×rateT=T\times rate。如果 T>epsT> eps,返回步骤 2。

代码

#include <stdio.h>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps=1e-3;
struct point{
	double x,y,z;
	point operator - (const point a) const
	{
		return {x-a.x,y-a.y,z-a.z};
	}
	double len()
	{
		return sqrt(x*x+y*y+z*z);
	}
}p[105];
int n;
double solve()
{
	double T=1000.0,rate=0.99999;
	point ansp=p[1];
	int cur;
	double ans=((point){200000,200000,200000}).len();
	while (T>eps)
	{
		cur=1;
		for (int i=1;i<=n;++i)
		{
			if ((ansp-p[i]).len()>(ansp-p[cur]).len()) cur=i;
		}
		ans=min(ans,(ansp-p[cur]).len());
		ansp.x+=(p[cur].x-ansp.x)*(T/1000);
		ansp.y+=(p[cur].y-ansp.y)*(T/1000);
		ansp.z+=(p[cur].z-ansp.z)*(T/1000);
		T*=rate;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
	{
		scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
	}
		
	printf("%.15lf",solve());
        return 0;
}