启发式BFS算法讲解及例题
引入
考虑这样一个问题,一张地图,有一个起点和终点,有障碍物,可以向上下左右走,求起点到终点的最短路。
这是普通的BFS遍历到的范围:

明显就多遍历了很多没用的点。
启发式搜索
而这样就显得不太聪明。
BFS是从一个点向外扩张,四面的扩张是毫无目的的,我们可以在搜索的过程中,给定要找的目标。由于最后的最短路长度只有一个值,所以不是最短路径上的点再去搜索他就是在浪费时间,可以用贪心的思想想一下,距离终点更近的点就先搜索。
这样有优先级的数据结构,不难想到——优先队列。
这样进行搜索的范围大致如下:

估计函数
我们需要每个点一个值,表示这个点找到终点的代价,计算代价的这个函数称为估计函数
估价函数
f
(
x
)
=
g
(
x
)
+
h
(
x
)
f(x)=g(x)+h(x)
f(x)=g(x)+h(x),
g
(
x
)
g(x)
g(x)表示从起点(初始状态)到当前点的花费,
h
(
x
)
h(x)
h(x)表示当前点到终点(目标状态)的花费。
无向图的h函数
无向图对于大多数题,一般使用曼哈顿距离估价。
即:
h
(
x
)
=
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
h(x)=|x_1-x_2|+|y_1-y_2|
h(x)=∣x1−x2∣+∣y1−y2∣
有向图的h函数
有向图的 h h h函数为反图的最短路到当前点的值。
至于函数
g
(
x
)
g(x)
g(x),就是从起点到现在这个点所用的代价(
d
i
s
dis
dis)。
因此,我们可以用以下的格式存储每个点。
struct node{
int x;//任意类型,表示当前状态
int dis;//从起点到当前点的代价
friend bool operator<(node a,node b){
return a.dis+h(a.x)>b.dis+h(b.x);//优先队列的比较,计算后代价较小的先遍历
}
};
注意
估价函数计算出的代价值一定不能大于现实的值,宁愿估小,也不能估大。
比如一个点的现实代价本来非常小,就是最优解,但是“一不小心”估大了,许多比它代价更大的点都在他之前遍历了,有没有起到优化效果了。
例题
八数码难题
首先提一句:暴搜可过。
先看
g
g
g函数,问的是次数,那么每走一步次数加一,那么
g
(
x
)
g(x)
g(x)的值就是从给出的初始状态到当前状态所需的步数。
接着是
h
h
h函数,其实不用太过于严谨,可以将当前棋盘与目标棋盘不同的数量作为
h
(
x
)
h(x)
h(x)
接着用优先队列广搜就行了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
string t="123804765";
string s;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int diff(string s){
int res=0;
for(int i=0;i<s.length();i++)res+=(s[i]!=t[i]);
return res;
}
struct node{
string s;
int dis;
bool friend operator<(node a,node b){
return diff(a.s)+a.dis>diff(b.s)+b.dis;
}
};
unordered_map<string,bool>vis;
unordered_map<string,int>dis;
void bfs(){
priority_queue<node>q;
q.push(node{s,0});
vis[s]=1;
dis[s]=0;
while(!q.empty()){
node x=q.top();
q.pop();
if(x.s==t){
print(x.dis);
return;
}
char c[3][3];
int X,Y;
//在字符串转化为字符数组的同时找空格的位置
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
c[i][j]=x.s[i*3+j];
if(c[i][j]=='0'){
X=i,Y=j;
}
}
}
for(int i=0;i<4;i++){
int xx=X+dx[i];
int yy=Y+dy[i];
if(xx<0||yy<0||xx>=3||yy>=3)continue;
swap(c[xx][yy],c[X][Y]);
string p="";
for(int i=0;i<3;i++)for(int j=0;j<3;j++)p.push_back(c[i][j]);//转化为字符串
if(!vis[p]){
vis[p]=1;
dis[p]=x.dis+1;
q.push(node{p,dis[p]});
}
else if(dis[p]>x.dis+1)dis[p]=x.dis+1,q.push(node{p,dis[p]});//在访问过的情况下查看一下是否为最优情况
swap(c[xx][yy],c[X][Y]);
}
}
}
signed main(){
cin>>s;
bfs();
}
浙公网安备 33010602011771号