「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
最近刷到过许多迷宫问题,大多数都可以用深度优先遍历或者广度优先遍历来做。偶然间看到了启发式搜索。用启发式搜索来能做迷宫问题可以大大提高搜索效率。
启发式搜索
这里对启发式搜索做一个简单的介绍,需要更深的了解大家可以网上自行查找。对于深度优先搜索,就是把所有的情况全部列出来然后得出答案。而启发式搜索是可以回溯的一个算法,觉得这一条路走下去可能并不值得,于是会换一条路,如果走其他的走下去也不行,那么可能就再接着走原来的路。大大提高了搜索的效率。启发式搜索需要两个要点。一个是当前代价,也就是从原起点走到现在这个位子花费了多少。另一个是未来代价,从现在走起,走到终点可能需要花费多少代价。将这个两个代价加在一起为总代价。我们选择总代价最小的点走下去。每走一步,都要通过总代价的大小来判断是否值得走下去。
当前代价
从原起点开始,每走一步,当前代价加一。
未来代价
通过选取合适的代价函数来计算出未来代价。
曼哈顿距离
如果迷宫内只能上下左右四个方向移动,那么可以选取曼哈顿距离来计算未来代价。
欧几里得距离
如果迷宫内可以选择任意方向移动,那么可以选取欧几里得距离来计算未来代价。这样话可能会产生小数,可能会影响最优点选取。
迷宫实例
给定一个正方形迷宫,迷宫中存在障碍,无障碍的点记为0,有障碍的点记为1。起点为s,终点为e。
迷宫准备
#include<bits/stdc++.h>
using namespace std;
int R,C; //迷宫的长和宽
int Load[100][100]; //迷宫规模
int Loaded[100][100];//标记迷宫已经走过的点
int Loadend[100][100];//最终迷宫所有扩展了的路径
int S1,S2;//起始位子
int End1,End2//最终位子
struct load{
int x;
int y;
int cost;//总代价
int S;//用于计算到原起点所花代价。
//总代价大小的判断
bool operator < (const load &p)const
{
return cost > p.cost;
}
load(int x1,int y1,int S1){
x=x1;
y=y1;
S=S1;
cost=Cost()+S;
}
int Cost(){//计算预估代价
int i=abs(x-End1);
int j=abs(y-End2);
int k=i+j;
return k;
}
}
走迷宫所需要函数
int end(load a)//判断是否已经走到终点
{
if(a.x==End1&&a.y==End2)
return 1;
else
return 0;
}
int move(int x){
if(x>=0&&x<=R)//判断到达的这个点是否可以移动
return 1;
else
return -1;
}
priority_queue<load> fit;//使用优先队列存放符合条件的点。
int Fload(){
//将起始点放入到这列表中
load des(End1, End2, 0);
load start(S1, S2, 1);
fit.push(start);//将点压入到队列中
Loaded[S1][S2] = 1;//标记
while(!fit.empty())
{
//取出一位
load T = fit.top();
fit.pop();
if (end(T)) return T.S;
Loadend[T.x][T.y] = 1;//标记走过的路
Loaded[T.x][T.y] = 1;
if ((move(T.x - 1)!=-1)&&(Load[T.x-1][T.y]==0)) //上移
{
load up(T.x - 1,T.y, T.S + 1);
if (end(up)) return T.S+1;
if (Loaded[up.x][up.y] == 0){
fit.push(up);
}
}
if ((move(T.x +1)!=-1)&&(Load[T.x+1][T.y]==0)) //上移
{
load down(T.x + 1,T.y, T.S + 1);
if (end(down)) return T.S+1;
if (Loaded[down.x][down.y] == 0){
fit.push(down);
}
}
if ((move(T.y - 1)!=-1)&&(Load[T.x][T.y-1]==0)) //左移
{
load left(T.x ,T.y-1, T.S + 1);
if (end(left)) return T.S+1;
if (Loaded[left.x][left.y] == 0){
fit.push(left);
}
}
if ((move(T.y+1)!=-1)&&(Load[T.x][T.y+1]==0)) //右移
{
load right(T.x ,T.y+1, T.S + 1);
if (end(right)) return T.S+1;
if (Loaded[right.x][right.y] == 0){
fit.push(right);
}
}
}
return -1;
}
测试
int main(){
cin>>R;
C=R;
for(int i=0;i<R;i++){
for (int j = 0; j < R; j++){
char a;
cin>>a;
if(a=='0')
Load[i][j]=0;
else if(a=='1')
Load[i][j]=1;
else if(a=='s'){
S1=i,S2=j;
Load[i][j]=0;
}
else if(matrii[i][j]=='e'){
End1=i,End2=j;
Load[i][j]=0;
}
}
}
int n=Fload();
Load[End1][End2]=1;
cout<<"需要"<<n-1<<"步"<<endl;//起点的第一步不算
for(int i=0;i<R;i++){
for(int j=0;j<R;j++){
cout<<Loadend[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
一个6*6的迷宫。
最后的图是所有扩展过的点,并不是最优路线。只能得出最短需要几步。