@Junlier
2018-08-31T14:11:18.000000Z
字数 3145
阅读 2541
搜索——模拟退火
对于一些题目,无法直接算出答案或者想不到正解,想到随机找答案,那么模拟退火就是一种有系统方法的随机算法
- 数据范围不大但爆搜显然过不了的题目
- 有一定的求解规律但又不完全符合规律的题目
- 多峰函数题或者算数几何之类的题目
- 题目想不出,退火总比爆0好的题目
百度百科......
模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
- 把当前答案看成一只退火兔,一开始很急躁(温度很高),所以答案到处乱跑(这中间会记录最优解),最后兔子慢慢冷静下来(温度逐渐降低),就找到答案
- 利用rand来寻找答案接受劣解的波动范围(当然T也是一个决定性参数)
- 退火找答案之后,可能会有更优答案在自己周围很近的地方,所以rand去周围看一看是否更优
- 一开始根据自己的猜测来找一个答案“开始”点,可以降低一定时间复杂度......
我只讲大致的思想,具体实现根据模板题及luogu的题解讲述来学习吧
主要是懒
luogu板子题平衡点/吊打xxx
然后,我的优美代码......
#include<iostream>#include<cstdlib>#include<cstdio>#include<cmath>#include<cstring>#include<iomanip>#include<algorithm>#include<ctime>#include<queue>#include<stack>#include<vector>#define rg register#define il inline#define lst long long#define ldb long double#define cold 0.99#define N 1050#define RD T*((rand()<<1)-RAND_MAX)//rand一个-T到T的数,随机跳多远#define Time() (ldb)clock()/(ldb)CLOCKS_PER_SECusing namespace std;const int Inf=1e9;int n;ldb ansx,ansy,ans;ldb etlx,etly,etl;struct WEI{ldb x,y,m;}ljl[N];il int read(){rg int s=0,m=0;rg char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();return m?-s:s;}il ldb f(rg ldb xx,rg ldb yy){rg ldb res=0;for(rg int i=1;i<=n;++i){rg ldb dx=xx-ljl[i].x,dy=yy-ljl[i].y;res+=sqrt(dx*dx+dy*dy)*ljl[i].m;}return res;}int main(){srand(time(NULL));n=read();for(rg int i=1;i<=n;++i){scanf("%Lf%Lf%Lf",&ljl[i].x,&ljl[i].y,&ljl[i].m);ansx+=ljl[i].x,ansy+=ljl[i].y;}etlx=ansx=ansx/n,etly=ansy=ansy/n;etl=ans=f(ansx,ansy);while(Time()<0.3){rg ldb nans=etl,nx=etlx,ny=etly;for(rg ldb T=1000;T>=1e-16;T*=cold){rg ldb xx=nx+RD,yy=ny+RD;//模拟退火答案的波动范围rg ldb res=f(xx,yy);//找答案的ansif(ans>res)ans=res,ansx=xx,ansy=yy;//更新答案if(nans>res||exp((res-nans)/T)*RAND_MAX<rand())nans=res,nx=xx,ny=yy;}}printf("%.3Lf %.3Lf\n",ansx,ansy);return 0;}
有难度,luogu均分数据
#include<iostream>#include<cstdlib>#include<cstdio>#include<cmath>#include<cstring>#include<iomanip>#include<algorithm>#include<ctime>#include<queue>#include<stack>#include<vector>#define rg register#define il inline#define lst long long#define ldb long double#define N 25#define M 10#define Time() (ldb)clock()/(ldb)CLOCKS_PER_SECusing namespace std;const int Inf=1e5;int n,m;ldb tot,ans=Inf;ldb v[N],sum[N];ldb dp[N][N];il int read(){rg int s=0,m=0;rg char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();return m?-s:s;}il ldb PF(rg ldb x){return x*x;}il ldb sol(){for(rg int i=1;i<=n;++i)sum[i]=sum[i-1]+v[i];for(rg int i=0;i<=n;++i)for(rg int j=0;j<=m;++j)dp[i][j]=Inf;dp[0][0]=0;for(rg int i=1;i<=n;++i)for(rg int j=1;j<=m;++j)for(rg int k=0;k<i;++k)dp[i][j]=min(dp[i][j],dp[k][j-1]+PF(sum[i]-sum[k]-tot));ans=min(ans,dp[n][m]);return dp[n][m];}il void SA(rg ldb T){rg ldb nans=ans;for(;T>1e-3;T*=0.98){rg int xx=1+rand()%n,yy=1+rand()%n;if(xx==yy)continue;swap(v[xx],v[yy]);rg ldb res=sol();if(res<nans||exp((res-nans)/T)*rand()-RAND_MAX<0)nans=res;else swap(v[xx],v[yy]);}}int main(){n=read(),m=read();for(rg int i=1;i<=n;++i)v[i]=read(),tot+=v[i];tot/=m,sol();while(Time()<0.3)SA(1000);printf("%.2Lf\n",sqrt(ans/m));return 0;}