一、概述
初次接触迷宫广搜问题,整了几天才渐渐明白其中原理;附上自己的学习经验(适合初学者);
二、问题
输入:第一行输入一个整数,表示有几组数据; 第二行输入两个整数 行和列 (r ,c);接下来输入r行c列的矩阵(S是入口,E是出口,#代表墙,. 是可行区域)
输出:从入口S到E的最短路径 (假设输入数据必定有一条可行路径)
例如:输入:
2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
5 9
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########
输出:
5
9
三、深搜
1、基本思想:运用递归回溯遍历整个迷宫,计算出所有可行路径,再比较出最短的;
2、代码:
#include<stdio.h>
#include<string.h>
char a[100][100];
int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; //定义出在当前位置走下一步的四种情况
int r,c,s1,s2,minn,n;
void dfs(int x,int y,int count)
{
for(int i=0;i<4;i++) //每次位置的四种可能走的四周 都遍历一遍
{
int tx=x+dx[i]; //尽量在函数内定义tx,ty 避免全局变量在递归时的影响
int ty=y+dy[i];
if(tx>=0&&ty>=0&&tx<r&&ty<c&&(a[tx][ty]=='.'||a[tx][ty]=='E')) // 前四个判断是否越界,后面判断避免墙(#)
{
if(a[tx][ty]=='E') //先判断是否已经结束
{
if(count<minn) minn=count;
n=1;
}
else
{
a[tx][ty]='#'; //把走过的地方标记为墙,避免后面的搜索再走
/* for(int i=0;i<r;i++)
{
printf("\n");
for(int j=0;j<c;j++)
printf("%c",a[i][j]);
}
printf("\n");*/ //输出每步走的地图,这个很实用,观察每一次行走的记录地图,找出欠缺的部分
dfs(tx,ty,count+1); //回溯,把不符合的尝试路径清除,同时把墙(#)变为(.)
a[tx][ty]='.';
}
}
}
}
int main()
{
int m,i,j;
scanf("%d",&m);
while(m--)
{
memset(a,0,sizeof(a));
minn=100000;n=0;
scanf("%d%d",&r,&c); //输入 行 列
for(i=0;i<r;i++)
{
getchar(); //消除每一行输入后的回车,这个很关键
for(j=0;j<c;j++)
{
scanf("%c",&a[i][j]);
if(a[i][j]=='S')
{s1=i;s2=j;} //记录开始的坐标
}
}
dfs(s1,s2,0); //深搜调用,从S开始,count初始为0
if(!n)
printf("impossible\n");
else
printf("%d\n",minn+2); // +2是为了把起始S和结束E都算在总步数中
}
return 0;
}
四、广搜
1、基本思想:运用队列先进先出的特性可以更好的处理下次要走的方向; 广搜可以同时走三个方向,弹出一个方向就再存入弹出的方向能走的三个方向;如此第一次找到符合条件的就是最短路径;
2、代码(基本类似于深搜):
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int ,int> v; //这个是c++ pair运用, typedef自定义了一个pair类型的 v;(内部的两个类型可以任意定义,这里定义为两个整型)
queue<v> q; //相当于定义 类型为 pair的队列q
int r,c,count,s1,s2,dx[]={-1,0,1,0},dy[]={0,-1,0,1};
char a[100][100]; int b[100][100];
int bfs()
{
q.push(v(s1,s2)); //存入开始的位置
while(!q.empty()) //判断是否为空,没有满足的可走方向,就不会压入队列元素,就会结束循环
{
v p=q.front(); //定义v类型的p 来存储队列q的第一个元素,然后弹出,循环遍历判断存入,弹出位置可走的四个方向
q.pop();
for(int i=0;i<4;i++)
{
int tx=p.first+dx[i];
int ty=p.second+dy[i];
if(tx>=0&&ty>=0&&tx<r&&ty<c&&a[tx][ty]!='#'&&!b[tx][ty])
{
b[tx][ty]=b[p.first][p.second]+1;
// a[tx][ty]='#'; 标记没有作用
q.push(v(tx,ty));
if(a[tx][ty]=='E') return b[tx][ty]; //此处的判断应在b[tx][ty]的赋值之后,不然会导致输出结果为0的错误
}
}
}
}
int main()
{
int m,i,j;
scanf("%d",&m);
while(m--)
{
memset(a,0,sizeof(a)); count=0;
memset(b,0,sizeof(b));
while(!q.empty()) q.pop(); // 清空队列,在多次循环中有着很大作用
scanf("%d%d",&r,&c); //输入 行 列
for(i=0;i<r;i++)
{
getchar();
for(j=0;j<c;j++)
{
scanf("%c",&a[i][j]);
if(a[i][j]=='S')
{s1=i;s2=j;}
}
}
b[s1][s2]=1;
printf("%d\n",bfs());
}
return 0;
}
五、总结
在最短路径方面,广搜有着很大的优势,可以节省很多时间,毕竟不用像深搜一样遍历所有结果才能得到最短的;而且在一些迷宫搜索题目中给出的测试数据一般深搜是很难通过的;推荐使用广搜来寻找最短路径; 这才是搜索的入门阶段,希望可以继续探索更加有趣的难题,有自己的目标才是努力下去的动力,继续努力!