搜索,一种最基本,也是最重要的算法。无数高级思想都建立在搜索的基础上。当你学会搜索时,你就跨入了一个新的阶段。所以说,搜索,是每个编程人的进阶科目。
搜索,分为深搜(英文缩写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成员数量
//以上均为常用的队列函数。