模拟退火是用来解决上述问题的一种概率搜索算法,如果我们只是用普通的二分的话,那么可能遇到陷入A这个局部极大值的解,而无法找到B这个全局最大值。
而模拟退火的精髓就在于有一定的概率在A这个位置的时候接受D这个位置的解,从而越过了这个局部极大值。
算法模型:
应用到ACM的时候一般都是解决最优解问题,但是这个是有概率性的,可能AC也可能WA。但是只有退火设置的较好,正确性是特别高的。
E
(
x
n
e
w
)
E(x_{new})
E(xnew)为新解的贡献,
E
(
x
o
l
d
)
E(x_{old})
E(xold)为旧解的贡献,他们之差是负的时候,说明新解较差,以一定的概率接受新解。
Metropolis准则表明,在温度为T时,出现能量差为dE的降温的概率为P(dE),表示为:P(dE) = exp( dE/(kT) )。其中k是一个常数,exp表示自然指数,且dE<0。所以P和T正相关。这条公式就表示:温度越高,出现一次能量差为dE的降温的概率就越大;温度越低,则出现降温的概率就越小。又由于dE总是小于0(因为退火的过程是温度逐渐下降的过程),因此dE/kT < 0 ,所以P(dE)的函数取值范围是(0,1) 。随着温度T的降低,P(dE)会逐渐降低。
对于新点的产生,在温度特别高的时候左右波动的幅度特别大,之后在温度降低时逐渐变小,最后趋于平衡,这便是最优解。
如下图所示
普通的模拟退火不会接受差解,这样就不存在错过最优解。
完全的模拟退火会接受差解,这样可能得不到最优解。
降温一般选取0.97~0.99,越高越好,但是时间也会增大。
2018 ACM-ICPC 南京现场赛:最小球覆盖:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#define PI acos(-1.0)
#define inf 0x3f3f3f3f
#define eps 1e-6
using namespace std;
const int NUM=30;
struct point{
double x,y,z;
}A[35];
double MAX(double a,double b){
return a>b?a:b;
}
double MIN(double a,double b){
return a<b?a:b;
}
double dist(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));
}
int main()
{
int n,i,j,k;
while(scanf("%d",&n),n){
for(i=0;i<n;++i){
scanf("%lf%lf%lf",&A[i].x,&A[i].y,&A[i].z);
}
point center;int lm=0;
center.x=center.y=center.z=0;
double dmax=100,ans=inf;
while(dmax>eps){
for(i=0;i<n;++i){
if(dist(center,A[i])>dist(center,A[lm]))lm=i;
}
double d=dist(center,A[lm]);
ans=MIN(ans,d);
center.x+=(A[lm].x-center.x)/d*dmax;//逐步向最远点移动,摆动的幅度随着dmax的减小而减小
center.y+=(A[lm].y-center.y)/d*dmax;
center.z+=(A[lm].z-center.z)/d*dmax;
dmax*=0.98;//0.97都不行至少0.98 , 降温
}
printf("%.5lf\n",ans);
}
return 0;
}
2019WHU网络赛:https://blog.csdn.net/Link_Ray/article/details/89173222