爬山算法和模拟退火算法
爬山算法
大体思路
爬山算法即是模拟爬山的过程,随机选择一个位置爬山,每次朝着更高的方向移动,直到到达山顶
具体操作
把当前的节点和要走的节点的值进行比较。 如果当前节点是最大的,那么不进行操作;反之就用要走的的节点来替换当前节点,从而实现向山峰的高处攀爬的目的。如此循环直到达到最高点。
缺点
会陷入局部最优解。只适用于计算几何等局部最优解集中的题目。
例题 POJ2420
#include<cstdio> #include<cmath> using namespace std; #define maxn 105 int n,go[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; double x[maxn],y[maxn],sx,sy; double calc(double x1,double y1){ double ans=0; for(int i=0;i<n;i++){ ans+=sqrt((x1-x[i])*(x1-x[i])+(y1-y[i])*(y1-y[i])); } return ans; } int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%lf%lf",x+i,y+i),sx+=x[i],sy+=y[i]; } sx/=n,sy/=n; double ans=calc(sx,sy),x1,y1,k; for(double i=1e4;i>1e-3;i*=0.9){ for(int i=0;i<4;i++){ x1=sx+go[i][0]*i,y1=sy+go[i][1]*i; k=calc(x1,y1); if(k<ans){//如果x1,y1比sx,sy更优,就更新sx,sy,ans ans=k,sx=x1,sy=y1; } } } printf("%.0lf",ans); return 0; }
模拟退火
模拟退火和爬山只有一点不同:
如果当前节点比要走的节点更优,则爬山一定不会跳到要走的节点
但模拟退火有一定的几率会跳到要走的节点,并且这个几率越来越小
#include<cstdio> #include<cmath> #include<cstdlib> using namespace std; #define maxn 50 int n,id; double x[maxn],y[maxn],z[maxn]; inline double dis(double x1,double y1,double z1,double x2,double y2,double z2){ return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)); } double calc(double x1,double y1,double z1){ double ans=dis(x1,y1,z1,x[0],y[0],z[0]),k; id=0; for(int i=1;i<n;i++){ if(ans<(k=dis(x1,y1,z1,x[i],y[i],z[i]))){ ans=k,id=i; } } return ans; } void work(){ double sx=0,sy=0,sz=0,ans,k,x1,y1,z1; for(int i=0;i<n;i++)scanf("%lf%lf%lf",x+i,y+i,z+i),sx+=x[i],sy+=y[i],sz+=z[i]; x1=sx/=n,y1=sy/=n,z1=sz/=n; ans=k=calc(sx,sy,sz); int si=0; for(double i=1e2;i>1e-7;i*=0.98){ x1=sx+(x[id]-sx)/k*i; y1=sy+(y[id]-sy)/k*i; z1=sz+(z[id]-sz)/k*i; k=calc(x1,y1,z1); if(k<ans)ans=k; if(k<ans||(1ll*rand()*rand()%100000)>++si)sx=x1,sy=y1,sz=z1;//如果不比原先优,则有一定几率去走 } printf("%.5lf\n",ans); } int main(){ srand(1231435); while(~scanf("%d",&n)&&n)work(); return 0; }