广度优先搜索

news2024/11/26 20:28:18

注:最近有些事所以请大家原谅

 那么废话讲完了┏ (゜ω゜)=☞

目录

一:认识广搜

广度优先搜索算法的搜索步骤一般是:

二:导入

广度优先搜索一般可以回答两类问题: 

三:基础应用

例题1:寻宝(topscoding主题库2638)

例题2:关系网络(topscodin主题库1458) 

四:图形类问题: 

 例题2:细胞

例题3:Satellite Photographs 卫星照片(topscoding主题库2653) 

例题4:红与黑(主题库2538) 

例5:最少步数(topscoding主题库2587) 


一:认识广搜

广度优先搜索BFS(Breadth First Search)也称为宽度优先搜索,它是一种先生成的结点先扩展的策略。

简单来说就是:从一个节点出发,先访问其直接相连的子节点,若子节点不符合,再问其子节点的子节点,按级别顺序依次访问,直到访问到目标节点。

广度优先搜索算法的搜索步骤一般是:

(1)从队列头取出一个结点,检查它按照扩展规则是否能够扩展,如果能则产生一个新结点。(2)检查新生成的结点,看它是否已在队列中存在,如果新结点已经在队列中出现过,就放弃这个结点,然后回到第

(1)步。否则,如果新结点未曾在队列中出现过,则将它加入到队列尾。

(3)检查新结点是否目标结点。如果新结点是目标结点,则搜索成功,程序结束;若新结点不是目标结点,则回到第(1)步,再从队列头取出结点进行扩展。

二:导入

可能对于上面的讲解,同学们还不是很了解什么是广度优先搜索,下面我们通过一个例子再来聊一聊什么是广度优先搜索。假设你自己经营了一家巧克力工厂,你需要找到巧克力销售商,这样你的巧克力才能卖出去。因此,你首先开始在你认识的朋友里面寻找,有没有巧克力销售商。如下图所示,你一共有三个好朋友,分别是马小云、刘小东和马小腾。

一开始的的查找很简单,你只需要创建一个朋友们的名单。然后依次检查名单中的每一个人,看看他是不是巧克力销售商。

 

首先,你开始查找你的朋友马小云,如果他是巧克力销售商,那么大功告成,查找也就结束了,如果他不是的话继续查找第二个朋友刘小东,同样如果他是巧克力销售商,那么大功告成,查找也就结束了,如果他不是的话继续查找第三个朋友马小腾,如果他是巧克力销售商,那么大功告成,查找也就结束了,如果他不是的话就表示你的朋友里面没有巧克力销售商。那接下来怎么办呢?很显然,接下来你就需要在查找你朋友的朋友有没有巧克力销售商了(是不是有点无脑枚举的意思?)。

 

同样,你需要首先把朋友的朋友的名单加入列表中,然后再一个个查找他们是否为巧克力销售商。这样一来,你不仅在朋友中查找,还在朋友的朋友中查找。别忘了,你的目标是在你的人际关系网中找到一位巧克力销售商。因此,如果马小腾不是巧克力销售商,就将其朋友也加入到名单中。这意味着你将在她的朋友、朋友的朋友等中查找。使用这种算法将搜遍你的整个人际关系网,直到找到巧克力销售商。这就是广度优先搜索算法。

广度优先搜索一般可以回答两类问题: 

第一类问题:从节点A出发,有前往节点B的路径吗?(在你的人际关系网中,有巧克力销售商吗?)

第二类问题:从节点A出发,前往节点B的哪条路径最短?(哪个巧克力销售商与你的关系最近?)刚才你看到了如何回答第一类问题,下面来尝试回答第二类问题——谁是关系最近的巧克力销售商。例如,朋友是一度关系,朋友的朋友是二度关系。

在你看来,一度关系胜过二度关系,二度关系胜过三度关系,以此类推。因此,你应先在一度关系中搜索,确定其中没有巧克力销售商后,才在二度关系中搜索。广度优先搜索就是这样做的!在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。顺便问一句:将先检查刘小东还是雷小军呢?刘小东是一度关系,而雷小军是二度关系,因此将先检查刘小东,后检查雷小军。你也可以这样看,一度关系在二度关系之前加入查找名单。

你按顺序依次检查名单中的每个人,看看他是否是巧克力销售商。这将先在一度关系中查找,再在二度关系中查找,因此找到的是关系最近的巧克力销售商。广度优先搜索不仅查找从A到B的路径,而且找到的是最短的路径(貌似这里又有一点贪心的感觉了)。

注意,只有按添加顺序查找时,才能实现这样的目的。换句话说,如果马小云先于雷小军加入名单,就需要先检查马小云,再检查雷小军。如果马小云和雷小军都是巧克力销售商,而你先检查雷小军再检查马小云,结果将如何呢?找到的巧克力销售商并非是与你关系最近的,因为雷小军是你朋友的朋友,而马小云是你的朋友。因此,你需要按添加顺序进行检查。有一个可实现这种目的的数据结构,那就是队列(queue),所以这也就是我们之前学习队列知识的原因所在。

所以,我们可以把上面的查找过程分层,按照层次遍历。每一层遍历完成后,再接着遍历下一层。

 

三:基础应用

例题1:寻宝(topscoding主题库2638)

小萱萱参加了一个“寻宝”游戏:在一排均匀排列的树上,被随机放置了一个“宝贝”,看谁能以最少的时间找到这个“宝贝”。每一个寻宝的人开始会站在第N(0≤N≤100000)棵树边,假设树有100000 棵,“宝贝”被放在第K(0≤K≤100000)棵树上,寻宝人有两种移动办法:步行和跳跃。假如寻宝人现在在第X棵树边,步行每秒可以从第X棵树向第X-1棵和第X+1棵树走去,跳跃可以让她在1秒内从第X棵树直接跳到第2*X棵树边(假如他有超能力完成跳跃,跳跃过.程中不能超过树的边界)。现在要求找到“宝贝”需要的最短时间。

输入格式:仅有两个整数N和K。

输出格式:最短时间

输入样例:5 17

输出样例: 4

解题思路:首先我们明确这是广度优先搜索的第二类问题,即从节点A出发,前往节点B的哪条路径最短?在本题中即从开始的树n开始,最少需要经过几次就可以找到第k棵树上的宝贝。我们首先从第n棵树出发,对于每一棵树,都有三种方式可以跳到下一棵树,即向前跳一步、向后跳一步和向后2倍距离跳。我们借用样例输入和输出来模拟下这个广度优先搜索的过程。

第1次搜索:5向三个方向搜索,分别是4,6,10;4,6,10进入队列,5出队列

第2次搜索:4向三个方向搜索,分别是3,5,8,去掉5;

6向三个方向搜索,分别是5,7,12,去掉5;

10向三个方向搜索,分别是9,11,20;3,8,7,12,9,11,20进入队列,4,6,10出队列;

第3次搜索:3向三个方向搜索,分别是2,4,6,去掉4和6;

8向三个方向搜索,分别是7,16,9,去掉7和9;7向三个方向搜索,分别是6,8,14,去掉6和8;

12向三个方向搜索,分别是11,13,24,去掉11;9向三个方向搜索,分别是8,10,18,去掉8和10;11向三个方向搜索,分别是10,12,22,去掉10;20向三个方向搜索,分别是19,21,40;2,16,11,13,24,18,12,22,19,21,40进入队列,3,8,7,12,9,11,20出队列;

第4次搜索:2向三个方向搜索,分别是1,3,4,去掉3和4;16向三个方向搜索,分别是15,17,32;

这个时候我们发现已经找到目标节点了,即编号为17的这棵树,并且通过了4次搜索,因此找到“宝贝”需要的最短时间为4次。下面我们一起来用代码来模拟我们上面的这个过程。

整的代码如下所示:

#include <bits/stdc++.h>
using namespace std;
int t[100001],f[100001];  //数组t存储时间,数组f标记该树有没有跳过 
int n,k;     //n表示开始位置,k表示结束位置 
queue<int>shu;  //定义队列shu 
int bfs()  
{
    shu.push(n);   //开始位置存入队列中 
    t[n]=0,f[n]=1;  //一开始的时间为0,开始位置被标记为1 
    while(shu.empty()!=1)     //当队列非空时开始循环 
    {   
        int x=shu.front();    //x为队列的队首元素  
        if(x==k){return t[x];}  //如果队首等于结束位置,则表示找到了,返回时间 
        //如果没找到,分三种情况讨论,分别向前跳、向后跳和2倍向后跳
        if(x-1>=0&&f[x-1]==0)  //如果x-1>=0即可以向前跳,且前面这个树还没有去过 
        { 
            shu.push(x-1);    //x-1进入队列 
            t[x-1]=t[x]+1;    //时间加1 
            f[x-1]=1;      //x-1被标记 
        }
        if(x+1<=100000&&f[x+1]==0) //如果x+1<=100000即可以向后跳,且后面这个树还没有去过 
        { 
            shu.push(x+1);    //x+1进队列 
            t[x+1]=t[x]+1;    //时间加1 
            f[x+1]=1;       //x+1被标记 
        } 
        if(x*2<=100000&&f[x*2]==0)  //如果x*2<=100000即可以向后2倍跳,且后面这个树还没有去过 
        { 
            shu.push(x*2);    //x*2进队列 
            t[x*2]=t[x]+1;    //时间加1 
            f[x*2]=1;       //x*2被标记 
        }
        shu.pop();  //这个数已经被遍历完了,出队列 
    }
}
int main()
{  
    cin>>n>>k; 
        cout<<bfs();   //调用 
}

例题2:关系网络(topscodin主题库1458) 

有n个人,他们的编号为1~n,其中有一些人相互认识,现在x想要认识y,可以通过他所认识的人来认识更多的人(如果a认识b,b认识c,那么a可以通过b来认识c),求出 x 最少需要通过多少人才能认识y。

样例输入:第1行3个整数n、x、y,2≤n≤100,1<=x,y<=n;

接下来的n行是一个 n × n的邻接矩阵,a[i][j]=1 表示 i 认识 j,a[i][j]=0表示不认识。保证i=j时,a[i][j]=0,并且a[i][j]=a[j][i]。

样例输出:一行一个整数,表示 x 认识 y 最少需要通过的人数。数据保证x一定能认识y。

样例输入:

5 1 5
0 1 0 0 0
1 0 1 1 0
0 1 0 1 0
0 1 1 0 1
0 0 0 1 0

样例输出:

2

解题思路:首先我们明确这是广度优先搜索的第二类问题,即从节点A出发,前往节点B的哪条路径最短?结合本题样例输入5,1,5,第一个5表示接下来是一个5*5的矩阵,1表示从第1个开始,5表示第5个人结束。首先,1进入队列,搜索1认识的人,显然1只认识2。2入队列,1出队列。接着开始搜索2认识的人,由矩阵可知,2认识1,3,4,其中1已经被标记过了,因此3和4入队列,2出队列;接着开始搜索3认识的人,由矩阵可知,3认识2,4,2,4都已经被标记过了;接着开始搜索4认识的人,由矩阵可知,4认识2,3,5,目标节点5出现,搜索也就结束了。1通过2找到4,再通过4找到5,因此最少需要通过2个人。下面我们一起来用代码来模拟我们上面的这个过程。 

完整的代码如下所示:

#include<bits/stdc++.h>
using namespace std;
int a[101][101],f[101],t[101]; //a数组表示矩阵,f数组标记是否找过,t数组表示通过几个人 
int n,x,y;  //n表示共有多少人,x表示开始,y表示结束 
queue<int>bh; //定义队列 bh 
int bfs(int x) //从x节点开始搜索 
{   
    bh.push(x);  //x节点入队列 
    t[x]=0; f[x]=1;  //一开始通过0个人认识,把x节点标记为找过 
    while(bh.empty()!=1)  //如果队列非空 
    {   
        int i=bh.front();  //队首元素为i 
        if(i==y){ return t[i]-1;}  //如果i==y表示找到了,这个时候即通过了 t[i]-1个人 
        for(int j=1;j<=n;j++) //如果没有找到,看看这个人认识那些人 
        {   
            if(a[i][j]==1&&f[j]==0)  // a[i][j]==1表示认识并且这个人没找过 
            {  
                bh.push(j);      //把它认识的人放进队列 
                t[j]=t[i]+1;     //t的值加1 
                f[j]=1;        //这个人被标记成1 
            }
        }
        bh.pop();   //当前这个人已经搜索完了,出队列 
    }
}
int main()
{   
    cin>>n>>x>>y;
    for(int i=1;i<=n;i++)
    {   
        for(int j=1;j<=n;j++)
        {   
            cin>>a[i][j];
      }
    }
    cout<<bfs(x);  //调用bfs进行搜索 
}

四:图形类问题: 

例子:从一个 n * m的迷宫的左上角走到右下角,其中“.”表示该点能走通,“#”表示该点不能走通。要求按照右、下、左、上的方向遍历,请问:利用广搜思路,画出左上角走到右下角的遍历过程?

 例题1:快乐的马里奥

https://www.topscoding.com/p/2948

注:此题为topscoding题目

按照广搜的顺序依次填写数字

#include<bits/stdc++.h>
using namespace std; 
int f[110][110];
queue<int>x,y;
int n,m,num=1;
void bfs(int h,int l){
    x.push(h),y.push(l);
    f[h][l]=1;
    while(!x.empty()){
        int tx=x.front(),ty=y.front()+1;//向右移动 
        if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&f[tx][ty]==0){
            x.push(tx),y.push(ty);
            num++;
            f[tx][ty]=num;
        }
        
        tx=x.front()+1,ty=y.front();//向下移动 
        if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&f[tx][ty]==0){
            x.push(tx),y.push(ty);
            num++;
            f[tx][ty]=num;
        }
        tx=x.front(),ty=y.front()-1;//向左移动 
        if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&f[tx][ty]==0){
            x.push(tx),y.push(ty);
            num++;
            f[tx][ty]=num;
        }
        tx=x.front()-1,ty=y.front();//向上移动 
        if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&f[tx][ty]==0){
            x.push(tx),y.push(ty);
            num++;
            f[tx][ty]=num;
        }
        x.pop();
        y.pop();
    }
}
int main(){
    cin >> n >> m;
    bfs(1,1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout << f[i][j]<< " ";
        }
        cout << endl;
    }
    return 0;
}


优化后:使用方向数组


#include<bits/stdc++.h>
using namespace std;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int f[110][110];
queue<int>x,y;
int n,m,num=1;
void bfs(int h,int l){
    x.push(h),y.push(l);
    f[h][l]=1;
    while(!x.empty()){
        for(int i=0;i<4;i++){
            int tx=x.front()+dx[i],ty=y.front()+dy[i];
            if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&f[tx][ty]==0){
                x.push(tx),y.push(ty);
                num++;
                f[tx][ty]=num;
            }
        }
        x.pop();
        y.pop();
    }
}
int main(){
    cin >> n >> m;
    bfs(1,1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout << f[i][j]<< " ";
        }
        cout << endl;
    }
    return 0;
}

 例题2:细胞

一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如阵列:

0234500067
1034560500
2045600671
0000000089

有4个细胞。

输入格式:整数m,n(m行,n列)矩阵(m和n均不超过100)

输出格式:一个数字,表示细胞的个数。 

样例输入:

4 10
0234500067
1034560500
2045600671
0000000089

样例输出: 

4

解题思路:可能很多同学一拿到题目,对于样例的输出4都非常困惑,因此我们首先得弄明白题目的意思,为何输出的结果为4。题目里面说,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,因此我们对于样例输入第一行的2来说,首先第一行的2345和第二行的3456以及第三行的456组成了同一个细胞,这就是第一个细胞。接着第一行的67是同一个细胞,这是第二个细胞。接着第二行的1和第三行的2是同一个细胞,这是第三个细胞。最后,第二行的5和第三行的671以及第四行的89是同一个细胞,这是第四个细胞。综上所述,一共有4个细胞。那么如何利用代码找到这些细胞呢?首先我们输入这个矩形阵列,对于其中的非零元素开始搜索,搜索分为上下左右四个方向,如果下一个节点非0,则接着对这个点的上下左右进行搜索,并且把这个点标记为0,最后所有节点都为0,则这个搜索完成了,我们也找到了第一个细胞。接着,我们开始查找矩阵中下一个非零元素,开始搜索,每一个完整的搜索就代表一个细胞,因此搜索的总次数也就是细胞的个数。 

完整的代码如下:

#include<bits/stdc++.h>
using namespace std;
char f[101][101]; //为什么用char类型?
//样例输入的不是一个数字而是一串串的数字,无法用int类型 
queue<int>x,y;  //声明两个队列x和y
//题目中说该细胞的上下左右,假设该细胞的位置为(i,j),
//则这个细胞的上一个为(i-1,j);下一个为(i+1,j);左一个为(i,j-1);右一个为(i,j+1);
//因此上下左右四个方向可以表示成下面两个数组,方便计算 
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int m,n,num;  //m表示行,n表示列 
void bfs(int h,int l)  //h(行),l(列)表示起始值下标 
{   
    x.push(h); y.push(l);    //h和l入队列  
    f[h][l]='0';         //(h,l)赋值为0 
    while(x.empty()!=1)      
    {   
        for(int i=0;i<=3;i++)  //循环4个方向 
        {
          int sx=x.front()+dx[i],sy=y.front()+dy[i];  //上下左右四个方向 
          if(sx>=1&&sx<=m&&sy>=1&&sy<=n&&f[sx][sy]!='0') 
            //如果x的位置在1到m之间,y的位置在1到n之间,保证不越界,并且该点不是0 
          {
                x.push(sx);y.push(sy);f[sx][sy]='0'; //该点入队列,该点被置为'0' 
          }
        }
        x.pop();y.pop();  //队首出队列 
    }
}
int main()
{   
    cin>>m>>n;  
    for (int i=1;i<=m;i++)
    {  
        for(int j=1;j<=n;j++)
        {   
            cin>>f[i][j];  
        } 
    }
    for(int i=1;i<=m;i++)
    {   
        for(int j=1;j<=n;j++)
        {   
            if (f[i][j]!='0')  //如果是细胞,就把他相连的都变成0 
            {   
                bfs(i,j); //从不是0的点(i,j)出发,开始搜索 
                num++;  //完整搜索一次表示有一个细胞 
            }
        }
    }
    cout<<num;
}

例题3:Satellite Photographs 卫星照片(topscoding主题库2653) 

农场主约翰给他的农场买了W*H像素的卫星照片(1 <= W <= 80, 1 <= H <= 1000),希望找出最大的"连续的"(互相连接的)牧场。对于一个牧场的任何一对像素,其中一个像素如果能横向的或纵向的与属于这个牧场的另一个像素相连,这样的牧场称作是连续的。 (很容易创建形状稀奇古怪的牧场,甚至是围着其它圆圈的圆圈。)

每一张照片都数字化的抽象了,牧场区显示为"*",非牧场区显示为"."。下面是一个10 *5的卫星照片样例:

..*.....**
.**..*****
.*...*....
..****.***
..****.***

这张照片显示了大小分别为4、16、6个像素的连续牧场区。帮助农场主约翰在他的每张卫星照片中找到最大的连续牧场。

输入格式:第 1 行: 两个由空格分开的整数,W 和 H 。第 2 到 H+1 行: 每一行包含 W 个 "*" 或者".",代表卫星照片的横向行。

输出格式:一行为最大连续牧场的大小。

样例输入: 

10 5
..*.....**
.**..*****
.*...*....
..****.***
..****.***

样例输出: 

16

解题思路:我相信同学们一看到这种又是*号又是.的题目就很头疼,其实细心一点的同学早就发现了,这一题跟上一题的细胞很相似。细胞是上下左右,牧场中的像素是横向的或纵向的,所谓横向的或纵向的不就是上下左右吗?细胞这道题目是求有几个细胞,也就是几个连续区域,而我们这道题目是求那个连续区域最大。因此我们只要在每次搜索的时候加一个变量s,没搜索成功一次加1就可以了。然后在求出每次搜索的最大值即最大连续牧场的大小。 

完整的代码如下所示:

#include<bits/stdc++.h>
using namespace std;
char f[1005][85];  //牧场地图 
int dx[4]={-1, 0, 1, 0}, dy[4]={0, 1, 0, -1};  //上下左右四个方向 
int w,h,maxn;    //w示行,h表示列 
int bfs(int m,int n)  //m,n表示起始值下标 ,int类型 
{ 
    queue<int>x,y; 
    x.push(m); y.push(n);    //m和n入队列  
    f[m][n]='1';          //这个点找过,就标记成字符1 
    int s=1;  //第一个点即s初值为1 
    while(x.empty()!=1)      
    { 
        for(int i=0;i<=3;i++)  //循环4个方向 
        {
            int sx=x.front()+dx[i],sy=y.front()+dy[i];  //上下左右四个方向 
            if(sx>=1&&sx<=h&&sy>=1&&sy<=w&&f[sx][sy]=='*') 
            //如果x的位置在1到h之间,y的位置在1到w之间,保证不越界,并且该点是*,是连续的牧场 
            {
                x.push(sx);y.push(sy);f[sx][sy]='1'; //该点入队列,该点被置为1表示已经找过了 
                s++;  //找到一个,加1 
            }
        }
        x.pop();y.pop();  //队首出队列 
    }
    return s; //每次搜索返回牧场像素的连续个数s     
}
int main()
{ 
     cin>>w>>h;  
    for (int i=1;i<=h;i++)  //h和w不要写反了 
    { 
        for(int j=1;j<=w;j++)
        { 
            cin>>f[i][j];  
        } 
     }
    for(int i=1;i<=h;i++)
    { 
        for(int j=1;j<=w;j++)  
        {   
            if(f[i][j]=='*')  //如果是牧场,就看看它的区域是多大     
            { 
                maxn=max(bfs(i, j), maxn);  //保留最大值 
            }
        }
    }
    cout<<maxn;
}


例题4:红与黑(主题库2538) 

小明站在一个矩形房间里,这个房间的地面铺满了地砖,每块地砖的颜色或是红色或是黑色。小明一开始站在一块黑色地砖上,并且小明从一块地砖可以向上下左右四个方向移动到其他的地砖上,但是他不能移动到红色地砖上,只能移动到黑色地砖上。请你编程计算小明可以走到的黑色地砖最多有多少块。

输入格式:输入包含多组测试数据。每组输入首先是两个正整数W和H,分别表示地砖的列行数。(1<=W,H<=20)。接下来H行,每行包含W个字符,字符含义如下:‘.’表示黑地砖;‘#’表示红地砖;‘@’表示小明一开始站的位置,此位置是一块黑地砖,并且这个字符在每组输入中仅会出现一个。当W=0,H=0时,输入结束。

输出格式:对于每组输入,输出小明可以走到的黑色地砖最多有多少块,包括小明最开始站的那块黑色地砖。

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

样例输出: 

45
59
6
13

解题思路:我相信很多同学一看到这个题目就放弃了,因为这个样例太奇怪了,其实只要静下心来稍微看一看,就发现其实这道题目跟上面一道卫星的题目很相似。只不过是‘*’和‘.’变成了‘.’和‘#’号了。因此两道题目的解题思路完全一致,只不过本道题是多行测试数据而已,因此我们只要在之前代码的基础上稍作修改就可以了。

完整的代码如下所示: 

#include<bits/stdc++.h>
using namespace std; 
char f[21][21]; 
int dx[4]={-1, 0, 1, 0}, dy[4]={0, 1, 0, -1};  //上下左右四个方向 
int w,h,b,c;  
queue<int>x,y;   
int bfs(int m,int n){ 
    x.push(m); y.push(n);       //m和n入队列  
    int s=1;  //第一个点即s初值为1
    f[m][n]='#'; 
    while(x.empty()!=1)          
    { 
        for(int i=0;i<=3;i++)   //循环4个方向 
        {
            int sx=x.front()+dx[i],sy=y.front()+dy[i];  //上下左右四个方向 
            if(sx>=1&&sx<=h&&sy>=1&&sy<=w&&f[sx][sy]=='.') 
            {
                x.push(sx);y.push(sy);f[sx][sy]='#'; 
                s++;  //找到一个,加1 
            }
        }
        x.pop();y.pop();  //队首出队列 
    }
    return s;  
}
int main(){ 
    while(1){
        cin>>w>>h; 
        if(w==0&&h==0) return 0;  
        //memset(f,'#',sizeof(f)); 
        for(int i=1;i<=h;i++)
        { 
            for(int j=1;j<=w;j++)
            { 
                cin>>f[i][j];
                if(f[i][j]=='@')  //找到一开始的位置 
                { 
                    b=i,c=j; 
                }
            }
        }
        cout<<bfs(b,c)<<endl;
    }
}


例5:最少步数(topscoding主题库2587) 

在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”。有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字。他的同桌平时喜欢下围棋,知道这件事后觉得很有趣,就想试一试,在一个(100*100)的围棋盘上任选两点A、B,A点放上黑子,B点放上白子,代表两匹马。棋子可以按“日”字走,也可以按“田”字走,俩人一个走黑马,一个走白马。谁用最少的步数走到左上角坐标为(1,1)的点时,谁获胜。现在他请你帮忙,给你A、B两点的坐标,想知道两个位置到(1,1)点可能的最少步数。

输入格式:两行,每行两个正整数,表示马所在的坐标。

输出格式:两行,所走的最少步数。

样例输入:

12 16

18 10

样例输出:

8

9

解题思路:做过几道广搜的题目之后,同学们肯定有点感觉了。对于这道题目,很显然对于每个棋子有很多种走法,在不越界且未走过的基础上搜索。我们先在草稿纸上看看,一共有多少种走法,很显然一共有12种走法。至于具体的代码,我想同学们应该可以独立完成了。

完整的代码如下所示:

解法一:


#include<bits/stdc++.h>
using namespace std;
int di[13]={0,-2,-2,-1,1,2,2,2,2,1,-1,-2,-2},  //一共12个方向 
    dj[13]={0,1,2,2,2,2,1,-1,-1,-2,-2,-2,-1};
 
int c[105][105];   
bool f[105][105];              //标记是否走过 
int bfs(int x, int y)
{
    queue<int>qx, qy;
    qx.push(x),qy.push(y);
    memset(c,0,sizeof(c));  
    memset(f,0,sizeof(f));    //清零?为什么 
    f[x][y]=1,c[x][y]=0;      //标记该点走过,第一个点为0步 
    if(x==1&&y==1) return 0;
    while(!qx.empty())
    {
        for(int i=1;i<=12;i++) 
        {
            int a=qx.front()+di[i], b=qy.front()+dj[i];  
            if(a>=1&&b>=1&&a<=100&&b<=100&&f[a][b]==0)  //未越界,未走过 
            {
                c[a][b]=c[qx.front()][qy.front()]+1;  //步数加1
                qx.push(a),qy.push(b); //如果没有到终点,就开始搜索,入队列 
                f[a][b]=1;  //标记走过了 
                if(a==1&&b==1) return c[qx.front()][qy.front()]+1;  //也可以return c[a][b]或者 return c[1][1] 
            }
        }
        qx.pop(),qy.pop();
    }
}
int main()
{
    int n1, n2, m1, m2;
    cin>>n1>>m1>>n2>>m2;
    cout<<bfs(n1, m1)<<endl;
    cout<<bfs(n2, m2);
}




解法二: 

#include<bits/stdc++.h>
using namespace std;
bool f[105][105];
int c[105][105];
int dx[13]={0,-2,-2,2,2,-2,-2,-1,1,2,2,-1,1},
    dy[13]={0,2,-2,-2,2,1,-1,2,2,1,-1,-2,-2};
int bfs(int h,int l)
{
    queue<int> qx,qy;
    memset(c,0,sizeof(c));
    memset(f,0,sizeof(f));
    c[h][l]=0;f[h][l]=1;
    qx.push(h);qy.push(l);
    while(qx.empty()!=1)
    {
        for(int i=1;i<=12;i++)
        {
            int sx=qx.front()+dx[i],sy=qy.front()+dy[i];
            if(sx<=100&&sx>=1&&sy<=100&&sy>=1&&f[sx][sy]==0)
            {
                c[sx][sy]=c[qx.front()][qy.front()]+1;
                qx.push(sx);qy.push(sy);
                f[sx][sy]=1;
            }
            if(qx.back()==1&&qy.back()==1){return c[1][1];}
        }
        qx.pop();qy.pop();
    }
} 
int main()
{
    int a,b,c,d;
    cin>>a>>b>>c>>d;
    cout<<bfs(a,b)<<endl<<bfs(c,d);
}

这期完结了下期见bydy~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/573575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Electron 入门案例详解

目录 前言一、开发环境检查二、入门案例实现1.初始化项目2.安装electron包3.运行electron应用 三、创建第一个应用窗口1.准备页面2.创建窗口3.运行窗口 总结 前言 Electron 是一种基于 Node.js 和 Chromium 的框架&#xff0c;可以方便地创建跨平台的桌面应用程序。虽然 Elect…

jlink-v8刷固件及解决keil报错 j-link is defective,j-link clone

今天在调试STM32F407的程序时&#xff0c;下载程序时突然keil识别不到jlink了&#xff0c;还以为是驱动的问题&#xff0c;重新装了好几次驱动&#xff0c;结果还是不行&#xff0c;于是就网上找各种办法&#xff0c;最后决定重新刷固件。网上的方法很多&#xff0c;好多都失败…

Kotlin Compose Multiplatform 跨平台(Android端、桌面端)开发实践之使用 SQLDelight 将数据储存至数据库

前言 关于标题和文章主题 取标题的时候我还在想&#xff0c;我应该写 Compose 跨平台呢还是写 Kotlin 跨平台。 毕竟对于我的整体项目而言&#xff0c;确实是 Compose 跨平台开发&#xff0c;但是对于我这篇文章要说的东西&#xff0c;那其实也涉及不到多少 Compose 相关的内…

力扣sql中等篇练习(二十七)

力扣sql中等篇练习(二十七) 1 连续两年有3个及以上订单的产品 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below WITH T as (SELECT t.product_id,t.d,count(order_id) numFROM(SELECT order_id,product_id,…

es elasticsearch 九 索引index 定制分词器 type结构后期弃用原因 定制动态映射 动态映射模板 零停机重建索引

目录 索引index 定制分词器 Type底层结构及弃用原因 定制 dynamic mapping 定制dynamic mapping template 动态映射模板 零停机重建索引 生产环境应该度别名数据 索引index Put /index Stings 分片 Mapping 映射 Aliases 别名 增加 Put my_index2 { "se…

网络安全行业在经济下行期仍然稳步增长,快抓住风口入行

根据IDC《2022年第四季度中国IT安全软件市场跟踪报告》的数据&#xff0c;2022年下半年中国IT安全软件市场厂商整体收入约为23.8亿美元&#xff08;约合165.7亿元人民币&#xff09;&#xff0c;同比上升12.4%。结合全年数据&#xff0c;2022全年中国IT安全软件市场规模达到39.…

NodeJS 文件操作④

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言NODE内置模块 FS模块 CallBack API mkdir &#xff08;创建文件夹 异步&#xff09; rmdir&#xff08;删除文件夹 异步&#xff09; rm&#…

百度爱番番的线索如何自动导入至CRM系统中?

百度爱番番是什么&#xff1f; 百度爱番番是应用百度强大AI能力&#xff0c;帮助企业实现营销数字化、自动化、智能化&#xff0c;为企业提供拓客、集客、管客的一站式智能解决方案&#xff0c;助力企业营销变得专业且智能。 百度爱番番的线索如何自动导入至CRM系统中&#xf…

MKS SERVO4257D 闭环步进电机_系列2 菜单说明

第1部分 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口和RS485/CAN串行接口&#xff0c;支持MODBUS-RTU通讯协议&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&am…

数据结构和算法,在Python中的实现方式

部分数据来源&#xff1a;ChatGPT 数据结构 在计算机科学中&#xff0c;数据结构指的是计算机中用来存储和组织数据的方式。数据结构是为算法服务的&#xff0c;同一个算法在不同的数据结构上运行效率可能会有很大的不同。这就要求我们在解决问题时要根据具体情况选择合适的数…

前端中间件Midway的使用

一、 关于midway1. 解决什么痛点2. 期望达到什么效果 二、创建应用并使用1. 创建midway应用2. 认识Midway2.1 目录结构2.2 Controller2.3 路由2.4 获取请求参数2.5 Web中间件2.6 组件使用2.7 服务(service) 三、写到最后 一、 关于midway Midway 是阿里巴巴 - 淘宝前端架构团队…

别做重复低质的工作内容摸鱼了,18k大佬分享自动化测试秘籍

自动化测试面试真题&#xff08;附答案&#xff09; 一、编程语法题 1、python有哪些数据类型 2、怎么将两个字典合并 3、python 如何将json写到文件里? 4、 __init__和_new__区别? 5、什么是可变、不可变类型&#xff1f; 6、mysql注入点&#xff0c;用工具对目标站直接写入…

IDEA操作数据库并设置时区

目录 设置mysql的时区的方法&#xff08;提供三种选择&#xff09; 1、直接在advanced上配置serverTimezone属性值&#xff08;单次连接有效&#xff09; 2、进入MySQL客户端修改time_zone 3、直接修改MySQL的my.ini配置文件设置time-zone 使用Database 1、查看当前数据源…

uCOSii信号量的作用

uCOSii中信号量的作用&#xff1a; 在创建信号量时&#xff0c;Sem_EventOSSemCreate(1)用于分时复用共享资源&#xff1b; Sem_EventOSSemCreate(0)用于中断和任务间同步或任务之间的同步。 具体在使用时&#xff0c;需要灵活运用。在访问共享资源时&#xff0c;我喜欢用互…

【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(5月26日论文合集)

文章目录 一、检测相关(9篇)1.1 Energy-based Detection of Adverse Weather Effects in LiDAR Data1.2 Anomaly Detection with Conditioned Denoising Diffusion Models1.3 Mask Attack Detection Using Vascular-weighted Motion-robust rPPG Signals1.4 Improved Multi-Sca…

AccessShareLock pg cancel backend terminate backend

AccessShareLock 在PostgreSQL中&#xff0c;AccessShareLock是一种用于控制对数据库对象并发访问的锁类型。它是一种读锁&#xff0c;允许多个事务同时从同一个对象进行读取&#xff0c;但它阻止并发事务获取冲突的锁&#xff0c;比如写锁或独占锁。 当一个事务在对象上获取…

【指针的深刻理解】

如何看待下面代码中的a变量? #include<stdio.h> int main() {int a 0;//同样的一个a&#xff0c;在不同的表达式中&#xff0c;名称是一样的&#xff0c;但是含义是完全不同的&#xff01;a 10;//使用的是a的空间&#xff1a;左值int b a; //使用的是a的内容&#x…

Java jiraClient上传附件不能在浏览器预览的问题分析

最近测试的同学反馈问题说使用平台的报bug上传视频的附件以后&#xff0c;然后在jira上确没有办法通过点击附件进行预览&#xff0c;而需要下载下来才能够查看。但是如果是直接在jira上进行上传附件就不会有这个问题 如果说不了解具体原因的&#xff0c;其实就会觉得这个问题非…

【Linux】Top命令参数解释

TOP命令 这是一个Linux系统下 top 命令所输出的进程监控信息。以下是各列含义&#xff1a; top - 09:52:15&#xff1a;当前时间。 up 27 min&#xff1a;系统已经运行的时长。 2 users&#xff1a;当前有2个用户登录到系统上。 load average: 0.97, 0.41, 0.21&#xff1a;系…

Git进阶+Jenkins入门

文章目录 1 Git进阶——GitFlow工作流程1.1 master与develop分支1.1.1 master1.1.2 develop 1.2 feature分支1.3 Release分支1.4 hotfix分支1.1.3 1 Git进阶——GitFlow工作流程 1.1 master与develop分支 1.1.1 master master&#xff1a;发布上线分支&#xff0c;基于master打…