什么是启发式搜索(heuristic search
)
h(x)
。h(x)
是对当前状态x
的一个估计,表示 x
状态到目标状态的距离。
h(x) >= 0
;h(x)
越小表示 x
越接近目标状态;h(x) ==0
,说明达到目标状态。x
状态所花的代价,我们称为 g(x)
。
g(x)
就是我们实际要求解的问题。g(x)
就是从起点到 x
位置花的步数 ,h(x)
就是与目标状态的曼哈顿距离或者相差的数目;在最短路径中,我们的 g(x)
就是起点到 x
点的最短距离,h(x)
就是 x
点到目标结点的最短路或直线距离。F(x)=g(x)+h(x)
,作为我们的搜索依据。
F(x) = g(x)
的时候就是一个等代价搜索,完全是按照花了多少代价去搜索。比如 bfs
,我们每次都是从离得近的层开始搜索,一层一层搜 ;以及dijkstra
算法,也是依据每条边的代价开始选择搜索方向。F(x) = h(x)
的时候就相当于一个贪婪优先搜索。每次都是向最靠近目标的状态靠近。dfs
。A
算法。令F(x) = g(x) + h(x)
。(这里的 h(x)
没有限制)。虽然提高了算法效率,但是不能保证找到最优解,不适合的 h(x)
定义会导致算法找不到解。不具有完备性和最优性。A*
算法。该算法仅仅对A
算法进行了小小的修改。并证明了当估价函数满足一定条件,算法一定能找到最优解。估价函数满足一定条件的算法称为A*
算法。F(x) = g(x) + h(x)
。 代价函数g(x) >0
;h(x)
的值不大于x
到目标的实际代价 h*(x)
。即定义的 h(x)
是可纳的,是乐观的。怎么理解第二个条件呢?
x
走到目的地,那么 h(x)
就是你感觉或者目测大概要走的距离,h*(x)
则是你到达目的地后,发现你实际走了的距离。你预想的距离一定是比实际距离短,或者刚好等于实际距离的值。这样我们称你的 h(x)
是可纳的,是乐观的。h(x)
的选定,比如在接下来的八数码问题中,我们选择了曼哈顿距离之和作为 h(x)
,你也可以选择相差的格子作为 h(x)
,只不过搜索的次数会不同。当 h(x) 越接近 h*(x) ,那么扩展的结点越少!A*
算法流程open
列表中。open
列表。如果为空,则搜索失败。如果open
列表中存在目标节点,则搜索成功。open
列表中取出F
值最小的节点作为当前节点,并将其加入到close
列表中。close
列表中,则丢弃它open
列表中,则检查其通过当前节点计算得到的F
值是否更小,如果更小则更新其F
值,并将其父节点设置为当前节点。open
列表中,则将其加入到open
列表,并计算F
值,设置其父节点为当前节点。2
步骤。方格左下角为G(x)
为起点到x
的步数,右下角为H(x)
为x
到终点的预估值,这里选的是曼哈顿距离,右上角为F(x)=G(x)+H(x)
估价函数。绿色点为起点,黑点为墙,红点为终点。起点相邻四个点进open
表,并计算出F
值。
继续选择open
列表中F
值最小的节点,此时最小节点有两个,都为4
。这种情况下选取哪一个都是一样的,不会影响搜索算法的效率。因为启发式相同。这个例子中按照右下左上的顺序选取。先选择绿色节点右边的节点为当前节点,并将其加入close
列表。其相邻4
个节点中,有1
个是黑色节点不可达,绿色节点已经被加入close
列表,还剩下上下两个相邻节点,分别计算其F
值,并设置他们的父节点为黄色节点。
此时open
列表中F
值最小为4
,继续选取下方节点,计算其相邻节点。其右侧是黑色节点,上方1
号节点在close
列表。下方节点是新扩展的。主要来看下左侧节点,它已经在open
列表中了。根据算法我们要重新计算它的F
值,按经过2
号节点计算G(n) = 3
,H(n)
不变,所以F(n) = 6
相比于原值反而变大了,所以什么也不做。(后面的步骤中重新计算F
值都不会更小,不再赘述)
此时open
列表中F
值最小仍为4
,继续选取
此时open
列表中F
值最小为6
,优先选取下方节点。
此时open
列表中F值最小为6
,优先选取右方节点
此时open
列表中F值最小为6
,优先选取右方节点
此时open
列表中F
值最小为6
,优先选取右方节点。
此时我们发现红色节点已经被添加到open
列表中,算法结束。从红色节点开始逆推,其父节点为7
号,7
号父节点为6
号,6
号父节点为5
号,5
号父节点为2
号(注意这里5
号的父节点是2
号,因为5
号是被2
号加入到open
列表中的,且一直未被更新),2
号父节点为1
号,最终得到检索路径为:绿色-1-2-5-6-7-
红色。
Code
#include <bits/stdc++.h>
const int maxn=100+5;
struct Node{
int x,y,fx;
Node(){}
Node(int a,int b,int c){x=a;y=b;fx=c;}
bool operator <(const Node &a)const{
return fx>a.fx;
}
};
int dx[]={0,-1,0,1},
dy[]={-1,0,1,0};
int a[maxn][maxn],f[maxn],g[maxn][maxn],vis[maxn][maxn];
int n,m,k,sx,sy,ex,ey;
int h(int x,int y){//估价函数,曼哈顿距离
return abs(x-ex)+abs(y-ey);
}
void Astar(){
memset(g,0x3f,sizeof(g));
g[sx][sy]=0;//g函数,起点到当前点的步数也是曼哈顿距离
std::priority_queue<Node> q;
q.push(Node(sx,sy,0));
while(!q.empty()){
Node t=q.top();q.pop();vis[t.x][t.y]=1;//堆顶进入closed列表
for(int i=0;i<4;++i){
int x=t.x+dx[i],y=t.y+dy[i];
if(x<1 || y<1 || x>m || y>n || vis[x][y] || a[x][y])continue;
if(g[x][y]>g[t.x][t.y]+1)//(x,y)到起点的曼哈顿距离变小,可以重复进堆
g[x][y]=g[t.x][t.y]+1;
else continue;//否则(x,y)到起点的距离比当前小且已经在堆,不需要处理
if(x==ex && y==ey){
printf("%d\n",g[x][y]);return;
}
q.push(Node(x,y,g[x][y]+h(x,y)));
}
}
}
void Solve(){
scanf("%d%d%d",&m,&n,&k);
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
for(int i=1;i<=k;++i){
int x,y;scanf("%d%d",&x,&y);
a[x][y]=1;
}
Astar();
}
int main(){
Solve();
Astar();
return 0;
}
原文:https://www.cnblogs.com/hbhszxyb/p/12898664.html