题目大意:在三维平面上给出 n 个点,要你找出一个点使得这个点到这 n 个点的最远距离最小,输出这段距离。
题解思路:模拟退火 (正解:三分套三分套三分) 。
模拟退火算法本质上是一种随机概率寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。但是本片博客就不深入模拟退火算法的探究了,这里推荐两篇大佬博客,有兴趣的朋友可以去看下:
大佬博客一
大佬博客二
先上代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const double EPS=1e-8;
struct Point{
double x,y,z;
}Dots[MAXN];
int n;
double Distance(Point a,Point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
double Solve(){
double Step=200,ans=1e9,mt;
Point z=Point{0,0,0};
int s=0;
while(Step>EPS){
for(int i=1;i<=n;i++)
if(Distance(z,Dots[s])<Distance(z,Dots[i]))
s=i;
mt=Distance(z,Dots[s]);
ans=min(ans,mt);
z.x+=(Dots[s].x-z.x)/mt*Step;
z.y+=(Dots[s].y-z.y)/mt*Step;
z.z+=(Dots[s].z-z.z)/mt*Step;
Step*=0.9999;
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
double x,y,z;
scanf("%lf %lf %lf",&x,&y,&z);
Dots[i]=Point{x,y,z};
}
printf("%.7f\n",Solve());
}
很多人会对Step、EPS、0.9999这几个参数很难理解,但这就是模拟退火的精髓。
首先我想先讲下Boltzman 概率分布:
同一个温度,分子停留在能量小状态的概率大于停留在能量大状态的概率
温度越高,不同能量状态对应的概率相差越小,温度足够高时,各状态对应概率基本相同。
随着温度的下降,能量最低状态对应概率越来越大,温度趋于0时,其状态趋于1。
Step这个参数在迭代的过程中一直乘上0.9999,因此会一直变小,最后会无限贴近0,
当Step==0时,状态就不会再改变了。模拟退火正是基于这样的原理,凭借计算机的高速计算能力慢慢逼近正确答案,而Step、EPS、0.9999正是决定精度的参数。