蓝桥杯算法基础(37)BFS与DFS


           

DFS


一般搜索算法的流程框架
1.定义点集X为已经处理的点,点集F为已发现但尚未处理的点 
2.初始化点击X为空,点集只包含搜索的起点
3.只要点集F不为空,循环4~6
    4.从点集F中取出一个点v
    5.处理点v,把点v加入点击X
    6,遍历v的出边,对于每个v的邻居,若既不在点集X中也不再点集F中,则加入点集F
7.搜索结束,带点集X里的点是搜索过的点


先进后出:栈处理
简单解读: 每一个点位,找到附近的邻接点,表示发现但未处理的,将其放入栈中,由于栈是先进后出,
拿第一个栈元素作为搜索点,若是该点的临界点中已经有加入栈的元素,则这个临界点不能再放入栈,
也就是不能从该点访问这个邻接点,依此类推,直到所有点都被访问过,或是找到答案退出。
 

递归处理
简单解读:F为当前点的邻接点并且还没有搜过,X表示已经搜过并且标记过的点
每到达一个点,搜索它的临界点的其中一个,走完之后标记这个点表示已经走过
,若是这条路不同走不了了,点位就回溯,回到之前的点,继续找没走过的点
(即没有标记过的点),直到回到起点,再找除了这个点以外的其他没走过的支路,
按照之前的走法直到全部走完,或是找到答案后退出 
 


递归DFS的流程框架 
1.定义点集X为已经处理的点,
2.初始化点集为空
3.递归搜索点v(开始时搜索起点)
5.处理点v,把点v加入点集X
6.循环v的出边,对于每个v的邻居,则递归处理那个点 
7.搜索结束,点集X里的点是搜索过的点 


1-2-4
  |
  3
栈处理:处理顺序不可能1 2 3 4 
递归处理:处理顺序有可能1 2 3 4
 

*/
/*
DFS迷宫可达问题
  有一个大小为n*m的二维迷宫,其中#用来表示墙壁,
  *用来表示有路可走,每一次可向上走左右的任一方向走一步,
  问从左上角出发可否走到右下角。

**####
#**###
##**##
###**#
####**

该题目的数据范围较小,顾客考虑暴力搜索,每一次从一个没有
走过的点出发尝试向四周走,若可以走就一直走下去
 
我们每次都是选择一个没有被到达过的点开始,进行下一步的尝试,这个尝试是不是很像for循环?

但是怎么确定这个点有没有被到达过呢,我们需要一个二维标记数组,来表示,初始化为0,若该店已被到达过,则置为1
 
 进行一次常识的话只需要一个for循环即可,但是我们需要进行的可是若干次尝试,是不是很像递归函数

我们用函数bool dfs(int x,iny y)来表示从点(x,y)出发是否可以到达终点,可行则返回1,否则返回0 


*/

#include<bit/stdc++.h>
using namespace std;

const int maxn=505;//存图数组最大长度 
int dx[4]={0,0,-1,1};//四个方向 
int dy[4]={1,-1,0,0};
int mapp[maxn][maxn];//存图 
int flag[maxn][maxn];//是否走过 
bool check(int x,int y){//检查该点是否合法 
    if(x>0&&y>0&&x<n&&y<n&&mapp[x][y]=='*'&&flag[x][y]==0){
        return true;
        
        return false;
    }
    
}

int edx,edy; //终点的坐标 

bool dfs(int x,int y){
    if(!check(x,y)){//不合法,返回false 
        return false;
    }
    if(x==edx&&y==edy)//合法且等于终点,返回true 
    return true;
    
    flag[x][y]=1;//合法但不是终点,标记该点 
    for(int i=1;i<4;i++){//上下左右四个方向各自遍历 
        if(dfs(x+dx[i],y+dy[i]))
        return true; //若是找到终点,返回true,如此便一直到返回到dfs结束 
    }
    return false;
}
int main(){
    
int x,y;
    scanf("%d%d%d%d",&x,&y,&edx,&edy);
    if(dfs(x,y))
    scanf("true");
    else{
        scanf("false");
    }
    
}
 
 
 
连通块问题
多加四个方向,从上下左右四个方向加上左上,左下,右上,右下四个方向
不需要flag数组标记,走过就消除该点 
找有多少个联通区域 
 
#include<bit/stdc++.h>
using namespace std;

const int maxn=505;//存图数组最大长度 
int dx[4]={0,0,-1,1,1,1,-1,-1};//四个方向 
int dy[4]={1,-1,0,0-1,1,1,-1};
char mapp[maxn][maxn];//存图 
bool check(int x,int y){//检查该点是否为联通区域 
    if(x>0&&y>0&&x<n&&y<n&&mapp[x][y]=='@'){
        return true;
        
        return false;
    }
    
}

bool dfs(int x,int y){
    if(!check(x,y)){//不是就停止往下走 
        return;
    }
    
    mapp[x][y]='*';//走过就消除 
    for(int i=0;i<8;i++){//八个方向各自遍历 
        dfs(x+dx[i],y+dy[i]);
    }

}
int main(){
    int count=0;
     
    string c;
    for(int i=0;i<n;i++){
        cin>>c; 
        mapp[i]=c;
        
    }//输入图 
    
    
    
    
    for(int x=0;x<n;x++)
    {
        for(int y=0;y<n;y++){
            if(mapp[x][y]=="@"){
            count++;//记录连通区域的数量 
            dfs(x,y);//消除这一块儿联通区域 
            }        
        }
    }
}

BFS 

//广度(宽度)优先搜索 (BFS) 
广度优先,层次遍历,一层一层扩展
 
 BFS的实现方法
 通常采用STL容器中的队列queue实现
 当向队列中压入一个元素时,就像排队一样,新来的要排在末尾,
 当队列中取出一个元素时,取出的是队列中最早压入的那个,
 就像排队,先来的先走,所以队列的性质可以用四个字概括,先进先出 
 
 
 起点先入队,再出队,将起点临阶的点入队,
 再出队一个元素,再将这个元素邻接的还没入队的元素入队
 /*
  #include<queue>
  queue<int>q;
  q.push(3);
  */ 
/*  
给出一个大小为n*n的01方阵,如果两点间为0表示这是一条路,1表示一面墙壁,
现在要求你从方针左上角走到右下角,输出最短的经过的路径,保证答案唯一 

比如这就是一个5*5的迷宫
[0]  1   0   0   0
[0]  1   0   1   0
[0] [0] [0] [0] [0]
 0   1   1   1  [0]
 0   0   0   0  [0]
显然最短路径就是[]包围起来的

那么为了解决这道题我们首先应该解决最短路径,然后再这过程中记录路径即可

这道题有一个性质,那就是相邻点的转移只花费1的代价,而bfs便可以在这种条件下解决最短性质问题
 
 我们先抛开记录路径这一说,只考虑如何使路径最短
 按照层序遍历的思想,是不是如果有一个状态,先前出现过了,之后再出现的话,花费的代价一定比之前的大,
 因为先前的状态在搜索树中的层更偏上一点
 所以我们开一个bool vis[][]来记录这个状态有没有出现过,减少搜索规模也同时保证答案的正确性
 一个(x,y)的点可以向上下左右个扩展一次,花费代价均为1,所以我们对于每个点都要尝试进行四个方向的扩展,
 最终就一定嫩遍历到终点
而且根据答案最优性,一个状态最优是不是保证被较前的一个状态扩展到便可行
所以当一个(x2,y2)被(x1,y1)扩展到的时候,我们记录他的步数等于step[x2][y2]=step[x][y]+1
一个点只被有效入队一次,有效出队一次,有n*n个点,复杂度便是O(n*n)

  */

//由于BFS是一层层的遍历,因此不会出现一条路走到终点,但不是最短路径的情况。 

  #include<bits/stdc++.h>
  using namespace std;
  const int maxn=505;
  int mapp[maxn][maxn];//存图 
  int step[maxn][maxn];//层数,到起点的最短举例 
  int vis[maxn][maxn];//是否走过 
  int n,m;
  bool check(int x,int y){//是否可走,且没被标记过 
      if(x>0&&x<=n&&y>0&&y<=m&&mappp[x][y]==0&&vis[0][0]==0){
          return true;
      }
      return false;    
  }
  
  int dx[4]={0,0,1,-1};//四个方向 
  int dy[4]={1,-1,0,0};
  
  typedef pair<int,int>PII;//重命名PII记录坐标x,y 
  
  queue<PII>qq;//关于x,y的队列 
  PII st,ed;//起点,终点 
  int bfs(int x,int y){
    
    //如果有多个BFS,记得清空队列,避免下一次BFS延续上一次的点 
    while(!qq.empty()){
        qq.pop();
    }


      qq.push(st);//起点入队 
      vis[st.first][st.second]=1;//标记起点 
      step[st.first][st.second]=0;//起点步数为0 
  
  while(!qq.empty()){//入队,出队如此循环,便可遍历完整个图 
        PII t=qq.front();//从起点开始,出队 
        qq.pop();
        if(t.first==ed.first&&t.second==ed.second){//每一个出队的元素都检查一遍是否到达终点 
            return  step[t.first][t.second];//若是到达返回步数 
        }
        for(int i=0;i<4;i++){//四个方向走 
            int xx=t.first+dx[i];
            int yy=t.second+dy[i];
            if(check(xx,yy))
            {
                qq.push(make_pair(xx,yy));//将xx,yy变成pair入队,用make_pair(int,int); 
                vis[xx][yy]=1;//标记 
                step[xx][yy]=step[st.first][st.second]+1;//步数+1 
            }
        }      
  } 
  return -1;
  }  
  
  
  
  int main(){
      
      int T; 
      rteurn 0;
  }
  
  
  //如果要记录路径怎么办?
 
 刚才我们有提到,一个状态只会被有效扩展一次,那么我们只要记录这个点被谁扩展到即可,
 我们只需要从终点开始不断访问他的前驱节点即可,当然由于是反正推到终点的,
 所以我们应该将这些点放到栈中再弹出即可  
  
 开一个pair<int,mint>fre来记录即可
 
 最终只需要从终点反推去起点即可
  while(en.first!=0||en.second!=0){//将前一个点位入栈,到起点为止 
      s.push(en);
      en=fre[en.first][en.second];//更新前一个扩展的点位 
  } 
  
  s.push(make_pair(0,0));//最后再将起点入栈 
  while(!s.empty()){
      cout<<s.top().first+1<<" "<<s.top().second+1;//输出 
      s.pop();//出栈 
  }
 

 

/*

题目大意: 
有一个三维迷宫,求起点'S'到'E'的最少需要走几步,如果起点和终点不
连通免责输出"Trapped!" 

解题策略:
    可以利用BFS按层搜索的性质,从起点'S'出发开始搜索,
    记录每个节点所在的层数,若终点'E'在第k层,则起点'S'到终点'E'的距离是k
*/


  #include<bits/stdc++.h>
  using namespace std;
  const int maxn=505;

int maxp[maxn][maxn][maxn];//存三维迷宫 
int step[maxn][maxn][maxn];//步数 
int vis[maxn][maxn][maxn];//记录 
int n,m;
bool check(int x,int y,int z){//判断 
    if(x>0&&x<=n&&y>0&&y<=n&&z>0&&z<=n&&mapp[x][y][z]==0&&vis[x][y][z]==0)
    return true;
    
    return false;
}

int dx[6]={0,0,1,-1,0,0};//方向 
int dy[6]={1,-1,0,0,0,0};
int dz[6]={0,0,0,0,1,-1};
typedef pair(int,int)PII;
 
struct Node{//结构体,存坐标 
    int x,y,z;
    Node(int a,int b,int c){//初始化结构体 
        x=a;
        y=b;
        z=c;
    }
};

queue<Node> qq;
Node st;
int bfs(int x,int y){
        qq.push(st);
        while(!qq.empty()){
            Node t=qq.front();
            qq.pop();
            if(t.x==edx&&t.y==edy&&t.z==edz){//判断出队的是不是终点 
                return step[t.x][t.y][t.z];
            }
            
            for(int i=0;i<6;i++){
                    int xx=t.x+dx[i];
                    int yy=t.y+dy[i];
                    int zz=t.z+dz[i];
                    if(check(xx,yy,zz)){
                            qq.push(Node(xx,yy,zz));
                            vis[xx][yy][zz]=1;
                            step[xx][yy][zz]=stap[t.x][t.y][t.z]+1;//步数加1            
                    }        
            }
        }
    return -1;
}
 

 
 
问题:
N个城市,编号1到N,城市间有R条单项道路,每条道路连接两个城市,有长度和过路费两个属性,Bob只有k块钱,他想从城市1走到城市N,问最短共需要走多长的路,如果到不了N,输出-1
 
思路:从城市1开始深度优先遍历整个图,找到所有能过到达N的走法,选一个最优的 
如何剪枝: 
1.最优化剪枝:如果当前已经找到的最优路径长度为L,那么在继续搜索的过程中,总长度已经大于等于L的走法,就可以直接放弃了,不用走到底了
2.可行性剪枝:如果当前到达城市的路费已大于k,或者等于k且没有到达终点,就可以直接放弃了
 

相关推荐

  1. 算法基础37BFSDFS

    2024-04-05 14:36:05       27 阅读
  2. -岛屿个数-bfs-dfs算法

    2024-04-05 14:36:05       54 阅读
  3. 刷题--python-22-dfs-bfs

    2024-04-05 14:36:05       44 阅读
  4. BFS

    2024-04-05 14:36:05       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-05 14:36:05       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-05 14:36:05       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-05 14:36:05       87 阅读
  4. Python语言-面向对象

    2024-04-05 14:36:05       96 阅读

热门阅读

  1. android studio中添加module依赖

    2024-04-05 14:36:05       35 阅读
  2. 其他元素

    2024-04-05 14:36:05       33 阅读
  3. [C++] 拷贝构造函数 && 深拷贝、浅拷贝

    2024-04-05 14:36:05       40 阅读
  4. 深入解析二叉树:理论与实践的完美结合

    2024-04-05 14:36:05       36 阅读
  5. 实验3-10 计算油费

    2024-04-05 14:36:05       37 阅读
  6. 什么是深度学习

    2024-04-05 14:36:05       34 阅读
  7. 实验6-1 近似求PI

    2024-04-05 14:36:05       34 阅读
  8. 【Python第三方库】lxml 解析器和xpath路径语言

    2024-04-05 14:36:05       41 阅读
  9. 多线程(31)StampedLock和ReadWriteLock

    2024-04-05 14:36:05       38 阅读
  10. MySQL【查询】

    2024-04-05 14:36:05       36 阅读
  11. Large Language Model Agent for Hyper-Parameter Optimization

    2024-04-05 14:36:05       35 阅读
  12. unblock with ‘mysqladmin flush-hosts‘ 解决方法

    2024-04-05 14:36:05       28 阅读