
什么是启发式搜索(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