搜索是一种非常实用的算法,分为DFS(深度优先搜索)和BFS(广度优先搜索)
DFS(深度优先搜索)
我们先不讲图的DFS,我们先讲一个小例子。(参考《啊哈算法》)
假设我们手上有标号为1~n的n张牌,我们面前有n个盒子,那么我想知道将每一张牌放入盒子中有多少种方法。
先从简单的三张牌开始。
- 我们先站在1号盒子面前,此时我们手中有标号为1,2,3的牌,我们可以随机放一张,这里为了便于之后讲解,先放一张1,往后移动。
- 之后我们站在了二号盒子面前,此时我们手中有2,3.这里我们放2,往后移动。
- 最后我们站在了三号盒子面前,我们手中只有3了,那么我们放3。
- 此时我们手上已经没有牌了,那么我们的放牌的方法的数目ans便加一,并且我们拿回3这张牌,并且回退到上一步,拿掉牌2。此时我们站在盒子2面前,手中有2,3。
- 这一次我们选择放3,之后再在3号盒子放2。ans加一。
看懂了这里之后我们便大致知道了DFS的操作过程是什么了。
- 首先我们要分析我们每一步能干什么,比如我们这里便是可以每一步放什么牌,而有些逃脱类的搜索题型,我们便分析我们在当前位置,我们能如何走。
- 我们要找到一种可解决问题的路径,或者无法解决问题。那么我们要么答案加一之后回溯,要么直接回溯到上一步。如果上一步还不行,那么我们继续回溯。这很明显是一个递归的过程。值得提及的是,我们一般会设置一个标记数组,用来标记当前位置是否已经放过了,避免产生重复。
- 最后统计结果。
void dfs()
{
if()判断是否需要回溯的条件。
{
return;
}
for()
{
循环当前位置所能做的操作。
if()判断是否越界或者条件不满足或者障碍物等情况。
continue;
if()满足条件的话
{
mark[][]=1;先将标记数组标记。
dfs();在递归下一步。
mark[][]=0;将标记取回。
}
}
return;
这个return十分关键,因为他代表着当前此种路径失败,不能走这条路,我们要换一条。
}
这是一套我总结出来的dfs模板,其中主要有几点值得注意。
- 我们需要抽象出每一步要做什么,以及抽象出回溯的条件。
- 我们要在一次失败的dfs或者某种已经成功的路径返回的时候将mark数组的标记取回。
- dfs我们求得的并非唯一解,他可以求出很多的解,其中最优解不能单纯靠上面的模板找出。
BFS(广度优先搜索)
BFS相较于DFS有一个优势,他的答案会是最优解,所以他的运行时间理论上会短于DFS。
那么BFS的思路是啥呢。记得我们的DFS他就相当于一条路走到黑,你不见南墙不回头。而BFS呢,他有一点是和DFS一样的,也就是搜索的共性,我们要抽象出在当前位置,我们能干什么。DFS是能走就走,莽就是了;而BFS就很有意思了,他是将当前位置所有能走的所有情况全部存入一个队列当中。
没找到啥好题,先将就着:POJ - 2251
这是一个三维的BFS题型,相较于二维的题型,其实很简单,就相当于多了些当前位置能进行的操作。
2020.2.27号添加说明
先将就这个题看。
**题意:**你困在一个三维的地方,你要逃出去。
**分析:**这是一道很简单的搜索题目,我们分析一下。首先,在每一个位置,我们每一步可以上下左右前后,那么我们就将上下左右前后中的位置并且满足条件的(即不是障碍物的地方)放入队列,步数你可以用结构体存,也可以开个数组,然后把当前位置出队。当队列的队首已经是E的位置的时候,我们就直接输出步数就OK了。
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int i,j,k;
int l,r,c;
char mm[31][31][31];
int mark[31][31][31];
int headi,headj,headk,taili,tailj,tailk;
struct weizhi
{
int x,y,z;
int time;
};
queue<weizhi>q;
void bfs(int x,int y,int z)
{
struct weizhi t;
mark[x][y][z]=1;
int nowx=x,nowy=y,nowz=z;
int nextx,nexty,nextz;
t.time=0;
t.x=x;
t.y=y;
t.z=z;
q.push(t);
while(!q.empty())
{
struct weizhi tt;
tt=q.front();
q.pop();
for(i=0; i<6; i++)
{
//遍历无法逃出
//6种情况,上下左右前后
if(i==0)
{
t.x=tt.x;
t.y=tt.y;
t.z=tt.z+1;
t.time=tt.time+1;
}
else if(i==1)
{
t.x=tt.x;
t.y=tt.y;
t.z=tt.z-1;
t.time=tt.time+1;
}
else if(i==2)
{
t.x=tt.x+1;
t.y=tt.y;
t.z=tt.z;
}
else if(i==3)
{
t.x=tt.x-1;
t.y=tt.y;
t.z=tt.z;
t.time=tt.time+1;
}
else if(i==4)
{
t.x=tt.x;
t.y=tt.y+1;
t.z=tt.z;
t.time=tt.time+1;
}
else
{
t.x=tt.x;
t.y=tt.y-1;
t.z=tt.z;
t.time=tt.time+1;
}
if(t.x<0||t.x>l||t.y<0||t.y>r||t.z<0||t.z>c)
continue;
//此为遇到障碍物不入队列
if(mm[t.x][t.y][t.z]=='#')
continue;
//此为可通点入队列
if(mark[t.x][t.y][t.z]==0&&(mm[t.x][t.y][t.z]=='.')||mm[t.x][t.y][t.z]=='E')
{
//printf("x:%d\ty:%d\tz:%d\ttime:%d\n",t.x,t.y,t.z,t.time);
mark[t.x][t.y][t.z]=1;
q.push(t);
if(mm[t.x][t.y][t.z]=='E')
{
printf("Escaped in %d minute(s).\n",t.time);
return ;
}
}
}
}
printf("Trapped!\n");
return;
}
int main()
{
while(~scanf("%d %d %d",&l,&r,&c))
{
if(l==0&&r==0&&c==0)
break;
getchar();
memset(mark,0,sizeof(mark));
for(i=0; i<l; i++)
{
for(j=0; j<r; j++)
{
for(k=0; k<c; k++)
{
scanf("%c",&mm[i][j][k]);
if(mm[i][j][k]=='S')
{
headi=i;
headj=j;
headk=k;
}
if(mm[i][j][k]=='E')
{
taili=i;
tailj=j;
tailk=k;
}
}
getchar();
}
if(i!=l-1)
getchar();
}
/*if(bfs(headi,headj,headk)!=-1)
printf("%d\n",bfs(headi,headj,headk));
else
printf("Trapped!\n");*/
bfs(headi,headj,headk);
while(!q.empty())
{
q.pop();
}
}
}
2020.2.24暂未总结出BFS模板以及未找到合适题型,留待日后修改。
总结
其实搜索总而言之是一种,特殊的暴力,因为他也是相当于有遍历所有情况的思想,但是他是一种优雅的暴力,在算法中,算很简单的算法。