关于搜索(上)
这一篇我来分享一下搜索,并且附带三道经典搜索题的题解,至于什么是搜索,就不让大家bdfs了,搜索一般有两种:
1.深度优先搜索(Depth-First-Search),简称深搜或DFS,一般用来进行图的遍历或暴搜出奇迹,暴搜应用范围很广,可以更直接的解决部分DP、模拟等题目,深搜大多是递归
2.宽度优先搜索(Breadth-First-Search),又叫广度优先搜索,简称宽搜、广搜或BFS,一般用来解决最少步数问题。
做题是学习的最好办法,让我们欢乐地做题吧
洛谷P1596就是一道经典的DFS遍历图的问题。
以字符串形式输入,输入完后,遍历每一个位置,如果是“W”,那么进行深搜。
深搜的思路是先把遍历到的点标记为‘.’,即不能走,这一步是必须的,否则会导致重复遍历这个点,造成死递归。
接着遍历八个方向,如果遇到‘W’则到那个点去搜索。
代码非常简短:
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,cnt,dx[8]={-1,-1,0,1,1,1,0,-1},dy[8]={0,1,1,1,0,-1,-1,-1};
char a[120][120];
void dfs(int x,int y){
a[x][y]='.';//标记,必须
for(int i=0;i<8;i++)
if(a[x+dx[i]][y+dy[i]]=='W')//向其它方向遍历
dfs(x+dx[i],y+dy[i]);//递归
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%s",a[i]+1);//输入,从1开始,防止下标越界(如果从0开始,那么有可能下标会变成-1)
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='W'){//发现‘W’,开始遍历
cnt++;
dfs(i,j);
}
}
}
cout<<cnt;
return 0;
}
上一道题缺了灵魂——回溯,接着是回溯经典题洛谷P1605
这道题把起点和终点给出了,从起点开始遍历即可。众所周知,DFS会一个劲的往下跑,不撞南墙不回头,但是这道题偏让他撞南墙,那就得回头了,我们称这个“回头”的过程叫回溯。回溯的原理也很简单,就是在搜索完后把标记恢复。我们在遍历时将这个点标记为不能走,防止它“回头”,但遍历完,我们将其标记为能走,下一次遍历他就会“回头”,就完成了回溯。
这道题就是搜到终点,cnt++,注意一个回溯就OK了,代码:
#include<iostream>
#include<cstdio>
using namespace std;
int tx,ty,cnt,n,m,t,ex,sx,sy,ey,a[10][10],dx[8]= {0,1,0,-1},dy[8]= {1,0,-1,0};
void dfs(int x,int y) {
if(x==ex&&y==ey) cnt++;//到终点了,cnt++
else {
a[x][y]=1;
for(int i=0; i<4; i++) {//就是正常得不能再正常的搜索
if(!a[x+dx[i]][y+dy[i]]) {
dfs(x+dx[i],y+dy[i]);
}
}
a[x][y]=0;//回溯,将之前标记为不能走的点重新标记为能走,让他撞了南墙后回头
}
}
int main() {
cin>>n>>m>>t>>sx>>sy>>ex>>ey;
for(int i=1;i<=m;i++) a[0][i]=a[n+1][i]=1;//输入,这里要有预操作,防止出界的
for(int i=0;i<=n+1;i++) a[i][0]=a[i][m+1]=1;
for(int i=1; i<=t; i++) {//设障
cin>>tx>>ty;
a[tx][ty]=1;
}
dfs(sx,sy);
cout<<cnt;
return 0;
}
接下来就轮到BFS了,先说一下BFS吧。
BFS用的是队列,把这个点的所有邻接点入队,遍历这些邻接点,再把邻接点的邻接点入队,以此类推
看一道题:洛谷P1443
起点已给出,从起点开始,把邻接点入队,记录每个点马走到那需要的步数,关于此题的重点在代码上有注释。
#include<bits/stdc++.h>
using namespace std;
const int dx[8]= {-1,-2,-2,-1,1,2,2,1};//方向数组
const int dy[8]= {2,1,-1,-2,2,1,-1,-2};
queue<int> xx,yy;//两个队列,xx表示点的x坐标,yy表示点的y坐标
int a[405][410],x,y,tx,ty,n,m,sx,sy;
void bfs() {
a[sx][sy]=0;
xx.push(sx);
yy.push(sy);
while(!xx.empty()) {
x=xx.front();
y=yy.front();
xx.pop();//如果萌新请注意,一定要注意出队!!否则就是大大的RE
yy.pop();
for(int i=0; i<8; i++) {
tx=x+dx[i];
ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m||a[tx][ty]!=-1) continue;//判断出界
a[tx][ty]=a[x][y]+1;//步数加一
xx.push(tx);//把邻接点的邻接点入队
yy.push(ty);
}
}
}
int main() {
cin>>n>>m>>sx>>sy;
memset(a,-1,sizeof(a));//这里把数组赋值为-1,到时候遍历不到的自己就是-1
bfs();
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++)
printf("%-5d",a[i][j]);//注意这道题有行宽限制
cout<<endl;
}
return 0;
}
结束了,这篇信息量还是挺大的,写了我半个多小时,题目不是很难,主要面对萌新,感谢收看。