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

news/2024/6/29 11:48:48 标签: 算法, 蓝桥杯, 宽度优先


           

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且没有到达终点,就可以直接放弃了
 


http://www.niftyadmin.cn/n/5470484.html

相关文章

Dynamo之雪花分形(衍生式设计)

你好&#xff0c;这里是BIM的乐趣&#xff0c;我是九哥~ 今天简单分享一些我收集的Dynamo的雪花分形案例吧&#xff0c;不过多讲解了&#xff0c;有兴趣的小伙伴&#xff0c;可以私信“雪花分形”获取案例文件&#xff0c;下面基本以分享为主&#xff1a; ******多图预警****…

grep无法使用完整的正则表达式

问题描述 grep无法使用完整的正则表达式&#xff0c;比如前置断言、后置断言、\d和\t、\n等 问题原因 使用了扩展正则&#xff0c;而不是perl正则。规则和perl正则不同 从文档上讲得很清楚&#xff1a; -E PATTERN is an extended regular expression 他是扩展表达式&#…

前端三剑客 —— CSS (第六节)

目录 内容回顾&#xff1a; 弹性布局属性介绍 案例演示 商品案例 布局分析 登录案例 网格布局 内容回顾&#xff1a; 变量&#xff1a;定义变量使用 --名称&#xff1a;值&#xff1b; 使用变量&#xff1a; 属性名&#xff1a;var&#xff08;--名称&#xff09;&a…

C#/WPF Inno Setup打包程序

Inno Setup介绍 Inno Setup 是一个免费的 Windows 安装程序制作软件。第一次发表是在 1997 年&#xff0c;现在已经更新到Inno Setup 6了。Inno Setup是一个十分简单实用的打包小工具&#xff0c;可以按照我们自己的意愿设置功能&#xff0c;稳定性也很好。 官方网址&#xff1…

哈佛大学商业评论 -- 第二篇:增强现实是如何工作的?

AR将全面融入公司发展战略&#xff01; AR将成为人类和机器之间的新接口&#xff01; AR将成为人类的关键技术之一&#xff01; 请将此文转发给您的老板&#xff01; --- 本文作者&#xff1a;Michael E.Porter和James E.Heppelmann 虽然物理世界是三维的&#xff0c;但大…

ubuntu18.04图形界面卡死,鼠标键盘失灵, 通过MAC共享网络给Ubuntu解决!

ubuntu18.04图形界面卡死&#xff0c;鼠标键盘失灵&#xff0c; 通过MAC共享网络给Ubuntu解决&#xff01; 1. 尝试从卡死的图形界面切换到命令行界面2. 进入bios和grub页面3. 更改Grub中的设置&#xff0c;以进入命令行4. 在命令行页面解决图形界面卡死的问题5. Mac共享WI-FI网…

ElasticSearch7.8的下载与安装和Kibana 7.8.0工具使用安装

1、ElasticSearch7.8.0下载 elasticsearch: 官方下载地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch 链接: https://pan.baidu.com/s/1wAKQoB3nhLhcnBlPfVOLxQ 提取码: t83n kibana: 链接: https://pan.baidu.com/s/156aD9zDdvUv8LFgDEIPoSw 提取码:…

PINN物理信息网络 | 非线性薛定谔方程的物理信息神经网络

前言 非线性薛定谔方程(Nonlinear Schrdinger Equation, NLS)是量子力学中描述波函数演化的一个重要方程,特别是在考虑介质的非线性效应时。它是薛定谔方程的一种推广形式,可以用于描述非线性介质中光波或量子粒子的传播,如光纤通信、玻色-爱因斯坦凝聚以及非线性光学等领…