【原创】浅谈搜索(上-dfs与队列)(迷宫,走迷宫)

64 篇文章 11 订阅

搜索,一种最基本,也是最重要的算法。无数高级思想都建立在搜索的基础上。当你学会搜索时,你就跨入了一个新的阶段。所以说,搜索,是每个编程人的进阶科目。

搜索,分为深搜(英文缩写dfs)和广搜(又叫宽搜,英文缩写bfs)。相比较来说,深搜较为简单,广搜更为管用。

深搜,就是有路就走,画成图片来看,就像植物的根,扎根很深,故名深搜。

广搜,我也不知道为什么叫广搜。

废话不多说,让我们在题目里更加深入的了解搜索吧!


1792:迷宫

总时间限制:
3000ms
内存限制:
65536kB
描述
一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n * n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。如果起点或者终点有一个不能通行(为#),则看成无法办到。
输入
第1行是测试数据的组数k,后面跟着k组输入。每组测试数据的第1行是一个正整数n (1 <= n <= 100),表示迷宫的规模是n * n的。接下来是一个n * n的矩阵,矩阵中的元素为.或者#。再接下来一行是4个整数ha, la, hb, lb,描述A处在第ha行, 第la列,B处在第hb行, 第lb列。注意到ha, la, hb, lb全部是从0开始计数的。
输出
k行,每行输出对应一个输入。能办到则输出“YES”,否则输出“NO”。
样例输入
2
3
.##
..#
#..
0 0 2 2
5
.....
###.#
..#..
###..
...#.
0 0 4 0
样例输出
YES
NO
这一道题,是搜索的入门题,(然而我错了35次),透过这一道题,我们可以了解到搜索的基本知识,详见代码:

#include<cstdio>
#include<cstring>
char a[101][101];
int k,n,temp,begina,beginb,enda,endb;
int dr[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int vis[101][101];
void guessing(int i,int j)
{
	//判断当前是否满足条件
        if(i>=n || j>=n || i<0 || j<0 || vis[i][j] || a[i][j]=='#') return;
	//判断是否满足终止条件
        if(i==enda && j==endb) 
	{
		temp=1;
		return;
	}
	vis[i][j]=1;//标志此地走过
	for(int p=0;p<=3;p++)
		guessing(i+dr[p][0],j+dr[p][1]);
//往四个方向走
}
int main()
{
	scanf("%d",&k);//一共运行几次
	for(int t=1;t<=k;t++)
	{
		memset(a,0,sizeof(a));//记得清空数组!
		memset(vis,0,sizeof(vis));//一定要清空!
		temp=0;//不然死的很惨!
		
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%s",a[i]);
		scanf("%d%d%d%d",&begina,&beginb,&enda,&endb);
		
		if(a[begina][beginb]=='#' || a[enda][endb]=='#') printf("NO\n");
		else 
		{
			guessing(begina,beginb);
			if(temp) printf("YES\n");
			else printf("NO\n");
		}
	}
}


在这个程序的基础上,我们可以做出更多的事情:


2753:走迷宫

总时间限制:
1000ms
内存限制:
65536kB
描述
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。
输入
第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用'.'表示,有障碍物的格子用'#'表示。
迷宫左上角和右下角都是'.'。
输出
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。
样例输入
5 5
..###
#....
#.#.#
#.#.#
#.#..
样例输出
9


在迷宫的基础上,增加了一个步数,但只需新增一个参数用以存储步数,调用自身时加一即可。

详见代码:

#include<cstdio>
#include<cstring>
char a[101][101];
int k,m,n,temp,enda,endb,min=999999;
int dr[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int vis[101][101];
void guessing(int i,int j,int t)
{

	if(i>=m || j>=n || i<0 || j<0 || vis[i][j] || a[i][j]=='#') return;
	if(i==enda && j==endb) 
	{
		if(t<min) min=t;
		t=0;
		return;
	}
	vis[i][j]=1;
	for(int p=0;p<=3;p++)	
		guessing(i+dr[p][0],j+dr[p][1],t+1);
}
int main()
{
	scanf("%d%d",&m,&n);
	for(int i=0;i<n;i++) scanf("%s",a[i]);
	enda=m-1,endb=n-1;
	guessing(0,0,1);
	printf("%d\n",min);
}</span>


由此,我们可以得到深度优先搜索的基本结构(回朔算法):

void dfs(int 参数1,int 参数2)
{
	if(不满足要求) return;
	if(达到目标值)
	{
		存储当前答案;
		return;
	}
	dfs(下一步);
}

 

但是仅凭dfs是不够的,因为每一步都要走,所以很耗时间,所以产生了bfs,广搜。了解广搜之前,我们先来了解一个对广搜很重要的数据结构:queue队列。

#include<queue>//调用队列所需头文件

queue <int> a;
queue <char> b;
struct Epic
{
	int x,xx;
	char xxx[maxn];
};
queue <Epic> c;//对队列的声明,包括自定义的结构体

int x,y,z;
a.push(x);//入队
//此时队列为x
a.push(y)//此时队列为x,y
a.push(z);a.push(y);a.push(x);
//此时队列为x,y,z,y,x
int k=a.front();//k=x,即队列第一个数值
int p=a.back();//p=x,即队列最后一个数值
a.pop();//出队,第一个成员出队即k,此时队列为:y,z,y,x
bool booll=a.empty();//队列a是否清空:已经清空返回1,否则返回0
int size=a.size();//队列a成员数量

//以上均为常用的队列函数。


 



 

 

 

 

 

 

 

 










  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值