开启掘金成长之旅!这是我参与「掘金日新计划 · 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
题目
题目大意
在三维直角坐标系中给出 个点,其中第 个点被描述为 。
找到一个点,最小化它距离给定的所有点的最远距离并输出。
思路
相当于空间中有 个半径为 的球使得给定的 个点要么在球内,要么在球面上。输出最小的 是多少。
可以使用模拟退火来求解,具体流程如下:
- 初始化温度 和温度下降系数 和终止条件 ,随便取一个点记为中心点 。
- 找到所有点中距离当前中心点距离最远的点 ,中心点 到 的距离就是新球的半径。用该半径试图对答案进行更新。
- 将中心点向距离中心点最远的点挪动,更新公式如下:
- 更新温度,。如果 ,返回步骤 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;
}