您的位置:首页 > 其它

深搜和广搜 迷宫最短路径

2017-11-04 20:33 344 查看

一、概述

初次接触迷宫广搜问题,整了几天才渐渐明白其中原理;附上自己的学习经验(适合初学者);

二、问题

输入:第一行输入一个整数,表示有几组数据; 第二行输入两个整数 行和列 (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;
}


五、总结

在最短路径方面,广搜有着很大的优势,可以节省很多时间,毕竟不用像深搜一样遍历所有结果才能得到最短的;而且在一些迷宫搜索题目中给出的测试数据一般深搜是很难通过的;推荐使用广搜来寻找最短路径; 这才是搜索的入门阶段,希望可以继续探索更加有趣的难题,有自己的目标才是努力下去的动力,继续努力!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息