zoukankan      html  css  js  c++  java
  • 神奇搜索算法A*

    A*

    A*是一种启发式搜索算法,又叫最佳图搜索算法。

    何谓启发式搜索?

    众所周知,计算机在执行搜索算法时是没开上帝视角的。因此,在搜索时,往往显得盲目,把所有可能的状态全部遍历,这种搜索我们统称盲目搜索。

    启发式搜索,顾名思义,其实就是给了盲目的计算机一点启发,一点智慧,利用这些来引导计算机减少搜索范围,大幅降低搜索复杂度。试想在BFS的过程中,在某一阶段某些状态成为正解的概率明显比别的状态要大,那我们就优先选择这些状态继续BFS,达到降低复杂度的目的。那么怎么计算这个概率呢?

    我们需要给计算机一些启发信息。不同的启发信息有不同的强度,既不能太强也不能弱,太强会导致得不到正解,而太弱又会导致优化效果不明显。都说了这么多了,那就来点具体的。

    如何进行启发式搜索?

    在A*之前,首先要提一下A算法,A*是A的一种特殊情形。

    这里我们要引入一个函数——“估价函数”。

    定义一个函数(f(n)=g(n)+h(n)),利用启发信息计算出(h),根据(f)函数的值找出当前搜索状态下的最有希望的节点进行扩展。其中(n)是一个状态,(f)就是估价函数,(g)(n)已经用掉的开销(可以理解做裸的BFS用到的信息),(h)是一个启发函数。

    启发函数在不同的情况下是有不同的计算方法的,这要因情况而异。

    为了更好定义A*算法,我们还要引入一些函数:(f^ *(n),g^ *(n),h^ *(n))

    首先我们要明确,上面提到的估价函数,是对待扩展节点的一个“估计”,是从启发信息计算而来的,因此从初始状态到正解对应的状态的花费不是实际花费。

    (f^ *(n)=g^ *(n)+h^ *(n))表示的是从搜索的起点经过(n)状态到达搜索目标的实际最小花费,因此,你可以把(f(n),g(n),h(n))看作这些带*的函数的估计值。

    A*算法定义为保证(h(n)<h^ *(n))的A算法,其保证能得到最优解。

    我们通过例题入手

    P1379 八数码难题

    题目描述

    在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。


    显然这题可以用双向BFS做(起点状态和中点状态模式相同),而且实际上裸的BFS这题也能过。。。

    不过既然是在讲A*,就讨论A*怎么解(哇,发现题解有一篇我校巨佬的IDA*QWQ)。

    为了使得(h)函数一定比(h^ *(n))小,我们考虑构造一个这样的函数。在起始状态,(h ^*(n))显然就是所有位置的数移动到目标状态的位置的所需步数。我们可以假定(h(n))(n)状态时不在目标位置的数的个数。因为显然,这些不再目标位置的数至少要通过一次移动才能到达目标位置。这是一个可行方案。

    贴代码:

    用了一个map来去除重复状态,其实matrix结构体里面随便咋重载都行,反正装的进去map就行。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
    struct matrix{
    	int a[5][5];
    	bool operator<(matrix x) const{
    		for(int i=1;i<=3;++i)
    		 	for(int j=1;j<=3;++j)
    		 		if(x.a[i][j]!=a[i][j]) return x.a[i][j]<a[i][j];
    		return 0;
    	}
    }st,f;
    inline int h(matrix a)
    {
    	int tmp=0;
    	for(int i=1;i<=3;++i)
    	 	for(int j=1;j<=3;++j)
    	 		if(a.a[i][j]!=st.a[i][j]) tmp++;
    	return tmp;
    }
    struct node{
    	matrix a;
    	int step;
    	bool operator<(node x) const{
    		return x.step+h(x.a)<step+h(a);
    	}
    	node(){};
    	node(matrix _a,int _step){a=_a,step=_step;}
    };
    priority_queue<node> q;
    map<matrix,bool> mp;
    int main()
    {
    	st.a[1][1]=1,st.a[1][2]=2,st.a[1][3]=3;
    	st.a[2][1]=8,st.a[2][2]=0,st.a[2][3]=4;
    	st.a[3][1]=7,st.a[3][2]=6,st.a[3][3]=5;
    	char c;
    	for(int i=1;i<=3;++i)
    		for(int j=1;j<=3;++j){
    			scanf("%c",&c);
    			f.a[i][j]=c-'0';
    		}
    	q.push(node(f,0));
    	while(q.size()){
    		node x=q.top();q.pop();
    		if(!h(x.a)){
    			printf("%d
    ",x.step);
    			return 0;
    		}
    		if(mp[x.a]) continue;
    		mp[x.a]=1;
    		int nx,ny;
    		for(int i=1;i<=3;++i)
    			for(int j=1;j<=3;++j)
    				if(!x.a.a[i][j]) nx=i,ny=j;
    		for(int i=0;i<4;++i){
    			int xx=nx+dir[i][0],yy=ny+dir[i][1];
    			if(xx>=1&&xx<=3&&yy>=1&&yy<=3){
    				swap(x.a.a[xx][yy],x.a.a[nx][ny]);
    				q.push(node(x.a,x.step+1));
    				swap(x.a.a[xx][yy],x.a.a[nx][ny]);
    			}
    		}
    	}
    	return 0;
    }
    

    如果要加ID的话会更快,这里不详细讲,具体做法跟一般IDBFS是一样的。


    P2324 [SCOI2005]骑士精神

    跟上面这道题完全一样,没区别,稍微改一下代码就好了。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define ll long long
    using namespace std;
    int dir[8][2]={{1,2},{-1,2},{2,1},{2,-1},{-2,1},{-2,-1},{1,-2},{-1,-2}};
    struct matrix{
    	int a[6][6];
    	bool operator<(matrix x) const{
    		for(int i=1;i<=5;++i)
    		 	for(int j=1;j<=5;++j)
    		 		if(x.a[i][j]!=a[i][j]) return x.a[i][j]<a[i][j];
    		return 0;
    	}
    }st,f;
    inline int h(matrix a)
    {
    	int tmp=0;
    	for(int i=1;i<=5;++i)
    	 	for(int j=1;j<=5;++j)
    	 		if(a.a[i][j]!=st.a[i][j]) tmp++;
    	return tmp;
    }
    struct node{
    	matrix a;
    	int step;
    	bool operator<(node x) const{
    		return x.step+h(x.a)<step+h(a);
    	}
    	node(){};
    	node(matrix _a,int _step){a=_a,step=_step;}
    };
    priority_queue<node> q;
    map<matrix,bool> mp;
    int main()
    {
    	st.a[1][1]=1,st.a[1][2]=1,st.a[1][3]=1,st.a[1][4]=1,st.a[1][5]=1;
    	st.a[2][1]=0,st.a[2][2]=1,st.a[2][3]=1,st.a[2][4]=1,st.a[2][5]=1;
    	st.a[3][1]=0,st.a[3][2]=0,st.a[3][3]=-1,st.a[3][4]=1,st.a[3][5]=1;
    	st.a[4][1]=0,st.a[4][2]=0,st.a[4][3]=0,st.a[4][4]=0,st.a[4][5]=1;
    	st.a[5][1]=0,st.a[5][2]=0,st.a[5][3]=0,st.a[5][4]=0,st.a[5][5]=0;
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		mp.clear();
    		while(q.size()) q.pop();
    		char c;
    		for(int i=1;i<=5;++i)
    			for(int j=1;j<=5;++j){
    				scanf(" %c",&c);
    				if(c=='*') f.a[i][j]=-1;
    				else f.a[i][j]=c-'0';
    			}
    		bool flag=0;
    		q.push(node(f,0));
    		while(q.size()){
    			node x=q.top();q.pop();
    			if(flag) continue;
    			if(x.step>15){
    				printf("-1
    ");
    				flag=1;
    			}
    			if(!h(x.a)&&!flag){
    				printf("%d
    ",x.step);
    				flag=1;
    			}
    			if(mp[x.a]) continue;
    			mp[x.a]=1;
    			int nx,ny;
    			for(int i=1;i<=5;++i)
    				for(int j=1;j<=5;++j)
    					if(x.a.a[i][j]==-1) nx=i,ny=j;
    			for(int i=0;i<8;++i){
    				int xx=nx+dir[i][0],yy=ny+dir[i][1];
    				if(xx>=1&&xx<=5&&yy>=1&&yy<=5){
    					swap(x.a.a[xx][yy],x.a.a[nx][ny]);
    					q.push(node(x.a,x.step+1));
    					swap(x.a.a[xx][yy],x.a.a[nx][ny]);
    				}
    			}
    		}
    	}
    	return 0;
    }
    

    就是跑的比较慢啦。

  • 相关阅读:
    css 中 overflow: hidden清楚浮动的真正原因
    Get、Post请求中文乱码问题有效解决方法
    安装PostgreSQL和卸载PostgreSQL
    PostgresSql日期字段比较大小
    关于多线程编程的一点思考
    Python 虚拟环境 pyenv、venv(pyvenv)、virtualenv之间的区别
    python常用命令
    python中各种文件打开模式a w r
    python-安装
    day1-安装配置
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11298282.html
Copyright © 2011-2022 走看看