启发式搜索

orz hhw posted @ 2015年6月15日 12:55 in 算法学习 with tags 模板 模拟退火 算法学习 A* 启发式搜索 bzoj , 717 阅读

一、模拟退火

步骤:

①选定一个初始状态和一个初始温度(变化幅度)T

②在T的范围内随机变化坐标

③计算新解与当前解的差△T'

④如果新解比当前解优(△T'>0),用新解替换当前解;

 否则以exp(△T'/T)的概率接受新解

⑤温度乘上一个小于1的系数,即降温

⑥如果温度小于一个给定值(精度)则退出,否则回到步骤②

BZOJ3680:模拟退火裸题

#include<cstdio>
#include<cmath>
#include<cstdlib>
#define N 11111
using namespace std;
int n,i;
double ansx,ansy,nowx,nowy,dis,nx,ny,T,now,x[N],y[N],w[N];
double Rand(){return (double)(rand()%20000)/20000.0;}
double dist(double xx,double yy){
	now=0;
	for(int i=1;i<=n;i++)now+=sqrt((xx-x[i])*(xx-x[i])+(yy-y[i])*(yy-y[i]))*w[i];
	if(now<dis)dis=now,ansx=xx,ansy=yy;
	return now;
}
int main(){
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%lf%lf%lf",&x[i],&y[i],&w[i]),ansx+=x[i],ansy+=y[i];
	ansx/=(double)n;ansy/=(double)n;T=100000;//变化幅度
	nowx=ansx=nowy=ansy;//初始位置
	dis=dist(ansx,ansy);//当前最优解
	while(T>0.001){
		nx=nowx;ny=nowy;
		nx=nx+T*(Rand()*2-1);
		ny=ny+T*(Rand()*2-1);//在当前位置的变化幅度内随机取一点
		now=dist(nowx,nowy)-dist(nx,ny);//计算当前解
		if(now>0||exp(now/T)>rand()){//如果当前解比最优解好那么取为当前解,否则以exp|当前解-最优解|/T的概率接受当前解
			nowx=nx;
			nowy=ny;
		}
		T*=0.98;//降低搜索范围(降温)
	}
	printf("%.3lf %.3lf",ansx,ansy);
}

BZOJ2428:神奇的模拟退火

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 11111
using namespace std;
double miv=1e30,ave=0;
int n,m,a[N],sum[N],belong[N];
void SA(){
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++)sum[belong[i]=rand()%m+1]+=a[i];
    double ans=0;
    for(int i=1;i<=m;i++)ans+=(sum[i]-ave)*(sum[i]-ave);
    double T=10000;
    while(T>0.1){
        int t=rand()%n+1,x=belong[t],y;
        if(T>500)y=min_element(sum+1,sum+m+1)-sum;
        else y=rand()%m+1;
        if(x==y) continue;
        double tmp=ans;
        tmp-=(sum[x]-ave)*(sum[x]-ave);
        tmp-=(sum[y]-ave)*(sum[y]-ave);
        sum[x]-=a[t],sum[y]+=a[t];
        tmp+=(sum[x]-ave)*(sum[x]-ave);
        tmp+=(sum[y]-ave)*(sum[y]-ave);
        if(tmp<=ans)belong[t]=y,ans=tmp;
        else if(rand()%10000>T)sum[x]+=a[t],sum[y]-=a[t];
        else belong[t]=y,ans=tmp;
        T*=0.9;
	}
    if(ans<miv) miv=ans;
}
int main(){
    srand(12345678);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),ave+=a[i];
    ave/=(double)m;
    for(int i=1;i<=10000;i++) SA();
    printf("%.2lf\n",sqrt(miv/m));
}

二、A*/IDA*

这两个神奇的算法一直都不会,今天上午听刘老师讲课稍微听懂了一点,下午又看到一道A*的题,做了一下,感觉好神奇啊

#include<cstdio>
#include<iostream>
using namespace std;
int ans[5][5]={{1,1,1,1,1},
    {0,1,1,1,1},
    {0,0,2,1,1},
    {0,0,0,0,1},
    {0,0,0,0,0}};
int xx[8]={1,1,-1,-1,2,2,-2,-2};
int yy[8]={2,-2,2,-2,1,-1,1,-1};
int T,i,j,x,y,k,a[5][5];
bool flag;
char ch[9];
bool judge(int a[5][5]){
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			if(a[i][j]!=ans[i][j])return 0;
	return 1;
}
bool ok(int a[5][5],int s){
	int v=0;
	for(int i=0;i<5;i++)	
		for(int j=0;j<5;j++)
			if(a[i][j]!=ans[i][j]){v++;if(v+s>k)return 0;}
	return 1;
}
void search(int s,int a[5][5],int x,int y){
	if(s==k){if(judge(a))flag=1;return;}
	if(flag)return;
	for(int i=0;i<8;i++){
		int nowx=x+xx[i],nowy=y+yy[i];
        if(nowx<0||nowx>4||nowy<0||nowy>4)continue;
        swap(a[x][y],a[nowx][nowy]);
        if(ok(a,s))search(s+1,a,nowx,nowy);
        swap(a[x][y],a[nowx][nowy]);
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		flag=0;
		for(i=0;i<5;i++){
			scanf("%s",ch);
			for(j=0;j<5;j++){
				if(ch[j]=='*')a[i][j]=2,x=i,y=j;
				else a[i][j]=ch[j]-'0';
			}
		}
		for(k=1;k<=15;k++){
			search(0,a,x,y);
			if(flag){printf("%d\n",k);break;}
		}
		if(!flag)puts("-1");
	}
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter