模拟退火可真是让人AC率--,但又能简单的骗到80+pts的好算法
-------------(手动分割)-------------------------------------------
模拟退火有什么用鸭?
模拟退火用来解决最优性问题,比如求个最大/最小值什么的。如果让求方案数还是老老实实写正(pian)解(fen)叭。
众所周知玄学贪心不需要证明。但如果想到的贪心是错的,那么会导致陷入局部最优解的情况。模拟退火则会以一定的概率跳出这个局部最优解来得到全局最优解。
当然不能保证100%一遍AC辣(后面会讲一些调参小技巧)
模拟退火思想
我们先来扯扯为什么这个算法叫做模拟退火。
顾名思义就是模拟物理退火的过程,但是博主是物(全)理(科)学渣所以并不会用物理语言来描述退火。
退火就是物体有高温缓慢降到低温的过程,在这个过程中,由于是缓慢降温,所以物体的分子会有序的排列。我们要模拟这个过程来使最终答案趋于最优解。
我们每次随机出一个状态来,如果这个状态的解比当前算过的最优解要优,则更新当前最优解,否则我们以一定的概率来接受这个劣解从而达到跳出局部最优的效果。
我们设定一开始的温度\(T\)(为了模拟物理上的退火qwq)是一个比较大的数,并设定一个略小于1的常数\(nxt\),通过\(T\times nxt\)来达到徐徐降温的效果。并设置终止温度\(T_0\),略大于0.设\(ans1\)(新解)-\(ans\)(当前最优解)为\(del\),接受劣解的概率就是\(e^{\frac{-del}{T}}\)。随着温度\(T\)的降低,接受概率也会降低,最终稳定在最优解附近。
就像酱紫
一些细节
1.我们每次随机新解是在原来的最优解的基础上进行的。如果当前劣解没有被接受,那么一定要记得回溯回原来的状态。(博主曾因此调参调到自闭但还是没有AC)
2.模拟退火这种骗分算法当然是要保证不\(TLE\)辣。别跑太多模拟退火全T了就不好了
3.确定其他部分没有写挂再来调参数,否则调到怀疑人生
关于调参的奇技淫巧
1.可以尝试srand(19260817)来增加rp
2.跑模拟退火的次数设置为质数有奇效,但别跑太多次,容易wa和T
3.调高温度\(T\)或\(nxt\)正确率会高一些(实测有效)
4.如果爆了一页OJ都不能过的话建议去洗脸或者老老实实打正解
代码:(以随机新的序列为例)
void SA()
{
double T=5500,nxt=0.999;
while(T>1e-15)
{
int x=rand()%n+1,y=rand()%n+1;
while(y==x) y=rand()%n+1;
swap(b[x],b[y]);//我们可以随机交换序列里的两个元素来得到一个新的序列
int qwq=getans();
int del=qwq-ans;
if(del<0)//如果比当前解要优就更新
ans=qwq;
else if(exp((double)(-del/T))*RAND_MAX>rand())//接受概率,没有为什么
ans=qwq;
else swap(b[x],b[y]);//如果这个劣解没有被接受就要回溯
T*=nxt; //徐徐降温
}
}