宽度优先搜索(BFS)也是搜索的手段之一。它与深度优先搜索类似,从某个状态出发搜索所有可达的状态。
与DFS不同的是搜索的顺序,宽度优先搜索总是先搜索离初始状态近的状态。也就是说,它是按照开始状态--->只需1次转移就可以到达的所有状态--->只需2次转移就可以到达的所有状态--->......,以这样的顺序开始搜索,对于同一个状态,宽度优先搜索只经过一次,因此时间复杂度:O(状态数 * 转移的方式)。
深度优先搜索隐式利用了栈进行计算(递归的基础便是栈),而宽度优先搜索则利用了队列。搜索时首先将初始状态添加到队列里,此后从队列的最前端不断取出状态,把从该状态可以转移到的状态中尚未访问过的部分加入队列,如此往复知道队列为空或找到问题的解。
队列的特性为先进先出,那么我们就可以知道所有的状态都是按照初始状态由近及远的顺序被遍历的。
迷宫问题:
给定一个大小为N×M的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格
的通道移动。请求出从起点到终点所需的最小步数。请注意,本题假定从起点一定可以移动
到终点。
输入
N=10, M=10(迷宫如下图所示。'#','.','S','G'分别表示墙壁、通道、起点和终点)
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#
输出
22
分析:起点状态其实就对应初始状态,起点可走周围四个方向(过程中需对是否越界,是否撞墙做出判断,不满足条件都为不可到达的状态),一旦遍历的点为终点这时经历的路径必为最短,因为宽度优先搜索是由近到远的,所以第一个来到终点的路径必为最短。过程中用二维数组d将到达每个点的最短路径保存下来。
由于要向四个方向移动,用dx和dy两个数组来表示四个方向向量。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
const int MAX = 100;
char maze[MAX][MAX+1];
int m,n;
int sx,sy;//起点坐标
int gx,gy;//终点坐标
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int d[MAX][MAX+1];
int bfs(){
queue<P> que;
//寻找起点和终点坐标
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(maze[i][j]=='S'){
sx=i;sy=j;
}
if(maze[i][j]=='G'){
gx=i;gy=j;
}
}
}
memset(d,INF,sizeof(d));
//将初始状态(起点)加入队列
que.push(P(sx,sy));
d[sx][sy]=0;
while(que.size()){
//取出队头
P p = que.front();
que.pop();
int nx=p.first,ny=p.second;
if(nx==gx&&ny==gy){
break;
}
for(int i=0;i<4;i++){
//移动后的位置记为(nx,ny)
nx=p.first + dx[i];ny=p.second + dy[i];
if(nx>=0&&ny>=0&&nx<m&&ny<n&&d[nx][ny]==INF&&maze[nx][ny]!='#'){
d[nx][ny]=d[p.first][p.second]+1;
que.push(P(nx,ny));
}
}
}
return d[gx][gy];
}
int main(){
cin>>m>>n;
for(int i=0;i<m;i++){
cin>>maze[i];
}
cout<<bfs()<<endl;
// cout<<sx<<" "<<sy<<endl;
// cout<<gx<<" "<<gy<<endl;
return 0;
}