模拟退火 学习笔记

djwj233

2020-10-25 12:59:19

Personal

## 模拟退火用来干什么 ~~用来骗分~~ 用来求解一个 **方案数量极大** 且 **可能不是单峰函数** 的问题。 ## 模拟退火的主过程 顾名思义,就是模拟金属的**退火** 定义:初始温度 $T_0$,当前温度 $T$,降温系数 $\Delta$,终止温度 $T_k$。 1. 赋初值 $T=T_0$(一个较大的数),$\Delta <1$,$T_k>0$。 2. 执行如下过程:(求最小值) > $\mathrm{ans\gets \infty}$ > $\mathrm{While\ T>T_k:}$ >> $\mathrm{now\gets Make\ a\ new\ solution}$ >> $\mathrm{calc\ val=W(now)}$ >> $\mathrm{If\ }val<ans:$ >>> $\mathrm{ans\gets val}$ >> $\mathrm{Else\ If\ }e^{\frac{ans-now}{T}}<rand():$ >>> $\mathrm{Restores\ now}$ >> $\mathrm{T\gets T\cdot \Delta}$ ## 例题 - [P3878 [TJOI2010]分金币](https://www.luogu.com.cn/problem/P3878) ```cpp #include<bits/stdc++.h> using namespace std; #define fo(v,a,b) for(int v=a;v<=b;v++) #define fo2(v,a,b) for(v=a;v<=b;v++) #define fr(v,a,b) for(int v=a;v>=b;v--) #define rg register #define il inline typedef long long ll; typedef double db; const int N=40; int n;int v[N]; int ans; int calc() { int sum1=0,sum2=0; fo(i,1,(n+1)>>1) sum1+=v[i]; fo(i,((n+1)>>1)+1,n) sum2+=v[i]; return abs(sum1-sum2); } void SA() { db t=2000,delta=0.99,t_k=1e-11; while(t>t_k) { int x=rand()%n+1,y=rand()%n+1; swap(v[x],v[y]); ll now=calc(); if(now<ans)ans=now; else if(exp((ans-now)/t)<(db)rand()/RAND_MAX) swap(v[x],v[y]); t*=delta; } } void solve() { cin>>n; fo(i,1,n)scanf("%d",&v[i]); srand(rand()); ans=calc(); fo(i,1,300)SA(); printf("%d\n",ans); } int main() { srand(1919810+time(0)); srand(rand());srand(rand()); int T;cin>>T; while(T--)solve(); return 0; } ```