关于搜索(上)

这一篇我来分享一下搜索,并且附带三道经典搜索题的题解,至于什么是搜索,就不让大家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;
}

结束了,这篇信息量还是挺大的,写了我半个多小时,题目不是很难,主要面对萌新,感谢收看。

 

posted @ 2022-07-04 20:33  唯私の超电磁砲  阅读(116)  评论(0编辑  收藏  举报