广度优先搜索 C++队列 P1141 01迷宫

本文介绍了广度优先搜索算法的基本概念,以及如何在C++中使用队列实现搜索过程。通过实际的迷宫问题,作者展示了初始版本和优化后的代码,强调了利用连通性减少重复探测的重要性。
摘要由CSDN通过智能技术生成

  广度优先搜索的定义      

        搜索算法算是比较常用的一个基础算法,主要应用在图论中,最常见的是迷宫问题。今天本人又回顾了以前学习过的搜索算法并且在洛谷上刷了几道题,想着拿其中一道题来写个体会。

        首先,广度优先搜索,顾名思义,尽可能多的向外扩散,比如我现在处在一个大棋盘中的某一个格子上,我就要把四周每个各自都探测一遍,然后在探测过的每一个格子的基础上继续向外扩散一圈,直到所有格子都被探测一边为止,我们完成了一个搜索。

        那么怎么用代码实现这个算法呢?用递归或者迭代两种方式都是可以的,我比较习惯利用队列通过迭代的方式实现,同样递归实际上也是要用到栈,这里简单介绍一下C++STL中队列的用法吧,用C语言手撸队列容易出现bug且费时间。

 C++队列

首先我们要包含一个文件头,队列的英文就是queue,所以非常好记

#include<queue>


queue <int> q;//中间<>内的就是要定义的队列的数据类型,q是变量名,可以自定义


int i=8;

q.push(i);//向队列中添加变量
q.push(8);//等价于上一行

q.pop();//从队列头取出一个变量,但是不会返回它的值
q.front()//返回队列第一个变量,但不会取出,所以这两个一般一起使用

q.empty()//用来判断一个队列是否为空,为空返回1

q.size()//返回队列的大小

 上述包括常用的队列操作函数,大家可以稍微了解一下

 如何利用队列实现广度优先搜索呢?下面给出一个迷宫题目来进行理解

有一个仅由数字 0 与 1 组成的 n×n 格迷宫。若你位于一格 0 上,那么你可以移动到相邻 4 格中的某一格 1 上,同样若你位于一格 1 上,那么你可以移动到相邻 4 格中的某一格 0 上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

 要完成这题就要从一个格子出发,不断的向外扩散,符合广度优先搜索的特性。

        假定我们要从x,y这个坐标开始向外扩散,我们先把该坐标入队列,然后探测它的四周有没有符合题目要求的点,如果有判断一下是否访问过该点,如果没有就入队列,直到四周的四个点全部判断完,循环上述步骤,直到队列为空,每次遇到符合条件并且没有访问过的点就将结果数量+1,话不多说,下面上代码

#include<iostream>
#include<queue>
using namespace std;
#define N 1008
struct d{//定义一个结构体存储坐标点
    int x;
    int y;
};
char maze[N][N];//迷宫
queue<d> q;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//用来转换方向
int main()
{
    d t;
    int m,n;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>maze[i][j];


    int x,y,sum;
    while(m--)//n次查询
    {
        cin>>x>>y;
        int result=1;
        int vis[N][N]={0};//用来存储一个点是否被访问过
        for(int i=0;i<=n;i++)
            vis[0][i]=1,vis[i][0]=1;
        t.x=x;
        t.y=y;
        q.push(t);//先将出发点入队列
        vis[x][y]=1;
        d p,dp;
        while(!q.empty())
        {
            p=q.front();
            q.pop();
            for(int i=0;i<4;i++)//探测四周
            {
                int x2=p.x+dir[i][0],y2=p.y+dir[i][1];
                if(x2>=1&&x2<=n&&y2>=1&&y2<=n)//未越界
                if(maze[p.x][p.y]!=maze[x2][y2])//符合题目要求的点
                {
                    dp.x=x2;
                    dp.y=y2;
                    if(!vis[p.x+dir[i][0]][p.y+dir[i][1]])//未访问过就入队列
                    {
                        q.push(dp);
                        vis[dp.x][dp.y]=1;
                        result++;//答案+1
                    }

                }
            }
        }
        cout<<result<<endl;
    }
    return 0;
}

        面对这题,很快我就写出来以上代码,但是提交上后显示三个测试样例超时,我一看测试用例m最大取到10的五次方,如果每次查询都要重新向外探测确实会超时。这个时候就需要对代码进行优化了,我们从一个点向外扩散的前提条件是下一个扩散点满足他和当前点的值不相同,那么对符合条件的下一个点来说,上一个也是它的可以扩散的点,就像图论中的连通图,如果有一条可以走通的路线,那么这个路线上的任何一个点都能到达这个路线上的任何一个点,利用这个原理对这道题来说,从一个点出发后探测过的所有可以走通的点的答案是相同的,他们同属于一个连通图,这个连通图内的所有点的大难都是相同的!!

        所以我们就不需要遇到一个查询点就重新探测一遍了,我们把用来记录是否被访问过的数组vis定义成全局的,再定义一个答案数组ans用来存储每个连通图的答案,用一个类似哈希的数组对一个访问过的坐标指定其连通图答案下表,如果后续有查询,我们直接利用下标在答案数组中以O(1)的时间复杂度就可以找到答案。干说理论可能有些抽象,下面是改进后的代码

#include<iostream>
#include<queue>
using namespace std;
#define N 1005
struct d
{
    int x;
    int y;
};
char maze[N][N];//用来存放迷宫
int ans[N*N],count=0,dr[N][N];//ans存储答案,dr用来存放答案下标
queue<d> q;//队列
int dir[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};//用来向四周探查
int main()
{
    d t;
    int m,n;
    cin>>n>>m;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            cin>>maze[i][j];

    int vis[N][N]= {0};
    int x,y,sum;
    while(m--)
    {
        cin>>x>>y;
        if(vis[x][y]==1)
        {
            cout<<ans[dr[x][y]]<<endl;
            continue;
        }
        int result=1;
        t.x=x;
        t.y=y;
        q.push(t);
        vis[x][y]=1;
        dr[x][y]=count;
        d p,dp;
        while(!q.empty())//对一个点搜索直到尽头
        {
            p=q.front();
            q.pop();
            for(int i=0; i<4; i++)//探查四周
            {
                int x2=p.x+dir[i][0],y2=p.y+dir[i][1];
                if(x2>=1&&x2<=n&&y2>=1&&y2<=n)//不越界
                    if(maze[p.x][p.y]!=maze[x2][y2])//可以移动
                    {
                        dp.x=x2;
                        dp.y=y2;
                        if(!vis[p.x+dir[i][0]][p.y+dir[i][1]])//未被访问过,入队列
                        {
                            q.push(dp);
                            vis[dp.x][dp.y]=1;
                            result++;
                        }
                        dr[x2][y2]=count;//标记答案下标
                    }
            }
        }
        ans[count++]=result;
        cout<<result<<endl;
    }
    return 0;
}

如有错误或者可以改进的地方,欢迎各位评论区留言

下面是一个简单的 C++ 实现,使用广度优先搜索算法来解决迷宫问题。 ```cpp #include <iostream> #include <queue> using namespace std; const int MAX_N = 100; // 迷宫的最大行数和列数 int n, m; // 迷宫的行数和列数 char maze[MAX_N][MAX_N + 1]; // 表示迷宫的字符数组 int sx, sy; // 起点坐标 int gx, gy; // 终点坐标 int d[MAX_N][MAX_N]; // 到起点的最短距离的数组 // 移动的四个方向(上、右、下、左) int dx[4] = { -1, 0, 1, 0 }; int dy[4] = { 0, 1, 0, -1 }; // 求从起点到终点的最短距离 int bfs() { queue<pair<int, int>> q; // 初始化 d 数组为 -1(表示未到达过该点) for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { d[i][j] = -1; } } // 把起点加入队列,并初始化到起点的最短距离为 0 q.push(make_pair(sx, sy)); d[sx][sy] = 0; while (!q.empty()) { pair<int, int> p = q.front(); q.pop(); // 如果已到达终点,则结束搜索 if (p.first == gx && p.second == gy) { break; } // 移动四个方向 for (int i = 0; i < 4; i++) { int nx = p.first + dx[i]; int ny = p.second + dy[i]; // 如果移动后到达的位置是空地且未到达过(d[nx][ny]=-1),则加入队列,并更新到该位置的最短距离 if (0 <= nx && nx < n && 0 <= ny && ny < m && maze[nx][ny] != '#' && d[nx][ny] == -1) { q.push(make_pair(nx, ny)); d[nx][ny] = d[p.first][p.second] + 1; } } } // 返回从起点到终点的最短距离 return d[gx][gy]; } int main() { cin >> n >> m; for (int i = 0; i < n; i++) { cin >> maze[i]; } // 在迷宫中寻找起点和终点 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (maze[i][j] == 'S') { sx = i; sy = j; } if (maze[i][j] == 'G') { gx = i; gy = j; } } } cout << bfs() << endl; // 输出从起点到终点的最短距离 return 0; } ``` 以上是一个简单的广度优先搜索解决迷宫问题的 C++ 实现,可以根据需要进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值