前言
博主这个暴力骗分选手get到了
人生的本质
前置
有一类函数,我们要求其的最低点/最高点
二分?三分?四五六七八九分??
哦凉凉了…
先介绍一个爬山
我们随机撒点,然后让这些点去做类似现实的爬山
即右边优就去右边,左边优就去左边
直到没有比他更优秀的了!
也就是相当于到山峰了吧
那么这个贪心的算法显然非常容易卡在一个局部最优解出不来了
哦怎么办呢…
按正常人的思维想,走到了一个最高处,就应该换个地方玩耍
玩耍也是要有思维的,怎么说也得要有点判断性嘛
怎么给程序加判断性呢
模拟退火就此出现
虽然听说退火日常打不过爬山??
玩法
扯皮一下物理知识
一个物体要降温,从内能高降低到内能低,并不是越快越好
我们需要一个徐徐降温的过程,来让其内部的微观结构结晶来最小化内能
当然以上无关
模拟退火就是基于以上操作
我们先给其一个温度
T
T
T,再给他一个降温系数
D
D
D,表示每次让温度变为
T
∗
D
T*D
T∗D
自然这个
D
D
D不能太小了
当一个物体内能大的时候,他有充分的时间与几率去做一些冒险的事情,调整其最优位置
反正他的分子能大,跑远了后面还是有回来的空间
反之分子能小的时候,他就不能变得活跃了
我们的模拟退火同样也要基于以上的思路
定义几个变量
x
x
x表示当前位置
Δ
x
\Delta x
Δx表示
x
x
x的增加量,其取值范围应该与
T
T
T成正比关系,因为分子能越大他能跑的地方是越远的
T
T
T表示当前温度
D
D
D表示温度变动系数,一般取在
[
0.95
,
0.99
]
[0.95,0.99]
[0.95,0.99]的区间内
Δ
F
\Delta F
ΔF表示解的变动值,等于
f
(
x
)
−
f
(
Δ
x
)
f(x)-f(\Delta x)
f(x)−f(Δx)
给一个初始解
x
x
x,徐徐降温并让其在合适范围内移动
取到下一个位置
x
1
=
x
+
Δ
x
x1=x+\Delta x
x1=x+Δx
计算当前位置
x
1
x1
x1的解
a
1
a1
a1
比较
a
1
a1
a1与
x
x
x位置的解
a
2
a2
a2
如果
a
1
a1
a1比
a
2
a2
a2优,此时显然可以进行移动
否则,我们要以一定的概率获得是否移动的信号
科学家不懈钻研过后获得了这个概率为
e
Δ
F
T
e^{\frac{\Delta F}{T}}
eTΔF
然后降温
反复执行上述过程直到
T
≤
e
p
s
T\leq eps
T≤eps,此时视为物体内能为
0
0
0
在实现的过程中,为了避免最后可能移到没有途中的解优秀的位置
我们可以记录一个中途的解的最大值来弥补
由于随机算法的不稳定性
我们可以多跑几次来确定解
例题
正常人都会选这个
bzoj3680: 吊打XXX
自然界一切物体都在向着能量大->能量小的位置移动
故我们要让这个模型整体能量尽量小
则让这个模型的重力势能尽量小
则让其在桌面上的绳子尽量短
那么就是让
∑
d
i
∗
l
e
n
i
\sum d_i*len_i
∑di∗leni尽量大,其中
l
e
n
i
len_i
leni表示长度
过程中有一个RAND_MAX的函数,可以取到Rand的最大值,用于生成一个
[
0
,
1
]
[0,1]
[0,1]之间的随机数来获取是否用目标解替换当前解
代码很好懂
本质在于调参
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define double long double
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=10005;
const double D=0.97;
const double eps=1e-12;
int n;
double ux[MAXN],uy[MAXN],di[MAXN];
inline double sqr(double u){return u*u;}
double calc(double u1,double u2)
{
double ret=0;
for(int i=1;i<=n;i++)
ret+=sqrt((sqr(u1-ux[i])+sqr(u2-uy[i])))*di[i];
return ret;
}
int main()
{
srand(20021127);
n=read();
double bx=0,by=0,best,ans;
for(int i=1;i<=n;i++)
{
ux[i]=read(),uy[i]=read(),di[i]=read();
bx+=ux[i];by+=uy[i];
}
bx/=n;by/=n;
best=ans=calc(bx,by);
int times=1;//退几次火
while(times--)
{
double nx=bx,ny=by;
for(double T=100000;T>eps;T*=D)
{
double ax=T*(rand()*2-RAND_MAX),ay=T*(rand()*2-RAND_MAX);
ax+=nx;ay+=ny;
double nxt=calc(ax,ay);
if(best>nxt)best=nxt,bx=ax,by=ay;
if(ans>nxt||exp((ans-nxt)/T)>((double)rand()/RAND_MAX))ans=nxt,nx=ax,ny=ay;
}
}
printf("%.3Lf %.3Lf\n",bx,by);
return 0;
}