18276 走迷宫 (BFS)

BFS基本模板

同DFS的问题引入,计算迷宫中的最短路径问题:方法是通过一层一层扩展的方法来达到目标位置,扩展时每发现一个点就将这个点加入到队列中去,直到达到目标位置。这个方法的核心思路是模拟路线,每当没写一个新的可能,就把这个可能入列,直到达到位置,然后在所以的队列中,取最短的队列即可解决问题。
基本模板
队列操作准备:

struct note
{
    int x;//表示横坐标
    int y;//表示纵坐标
    int s;//表示步数
};
struct note queue[2501];//由于地图是50*50的,所以队列总长度不会超过2500
int head,tail;
int a[51][51];//存储地图
int book[51][51]={0};//用来记录哪个点已经被走过了的数组
//最开始的时候需要对队列进行初始化,即将队列设置为空
head=1;
tail=1;
queue[tail].x=1;
queue[tail].y=1;
queue[tail].s=0;
tail++;
book[1][1]=1;
//然后从(1,1)开始,先尝试往右走到达了(1,2)
//具体的操作是y+1
tx=queue[head].x;
ty=queue[head].y;
//接着判定是否越界?
if(tx<1||tx>n||ty<1||ty>m)
{
    continue;
}
//再接着判断是否下一个点是障碍物或者已经走过?
if(book[tx][ty]==0&&a[tx][ty]==0)//如果没有走过,而且不是障碍物,那么这个点就是我们想要的点
{
    book[tx][ty]=1;//BFS的算法下,每一个点通常只入队一次。
    queue[tail].x=tx;
    queue[tail].y=ty;
    queue[tail].s=queue[head].s+1;//步数更新
    tail++;
}

刚才那一步是入列操作核心,我们发现从(1,1)可以到达(1,2)也可以到达(2,1),那么我们也需要将(2,1)也加入队列操作与刚才一致。在将这两点完全加入队列之后,(1,1)就没有用了,此时我们对(1,1)出列处理,出列的操作非常简单,只需:

head++;

表示前头的指向向后指了一位。而这样指了这么一位之后,就要重复刚才的过程了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
struct note
{
    int x;//表示横坐标
    int y;//表示纵坐标
    int f;//父亲在队列中的编号,本题不要求输出路劲,可以不需要f
    int s;//表示步数
};
int main()
{
    struct note queue[2501];
    int a[51][51]={0},book[51][51]={0};//记录地图,记录路径
    //定义一个表示方向的数组
    int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
    int head,tail;
    int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    scanf("%d%d%d%d",&startx,&starty,&p,&q);
    head=1;
    tail=1;
    queue[tail].x=startx;
    queue[tail].y=starty;
    queue[tail].s=0;
    queue[tail].f=0;
    tail++;
    book[startx][starty]=1;
    flag=0;//用来表示状态量,如果没有达到我们的目的地,那么就令其一直为0
    //当队列不是空的时候我们循环
    while(head<tail)
    {
        for(k=0;k<4;k++)
        {
            tx=queue[head].x+next[k][0];
            ty=queue[head].y+next[k][1];
            if(tx<1||tx>n||ty<1||ty>m)
            {
                continue;
            }
            if(a[tx][ty]==0&&book[tx][ty]==0)
            {
                //标记点已经走过
                book[tx][ty]=1;
                //queue的head是用来存储当前状态的,queue的tail是用来存储将要存储的东西的
                queue[tail].x=tx;
                queue[tail].y=ty;
                queue[tail].f=head;//f是用来记录当前路径的,如果题目有要求说需要求出这条路是怎么走的
                //就可以引入这个变量来表示
                queue[tail].s=queue[head].s+1;
                tail++;//准备下一次插入
            }
            //接着判断是否到达目标点,如果到了就停止扩展啦
            if(tx==p&&ty==q)
            {
                flag=1;
                break;
            }
        }
        if(flag)
        {
            break;
        }
        head++;
    }
    printf("%d",queue[tail-1].s);
    return 0;
}

宝岛探险问题

目前给出小人在由多个岛屿中组成的地图的初始位置,假定降落点的岛屿以海洋为边界,所有的都属于这个岛屿,求出岛屿的面积。
由于每个点只入队一次,那么这道题用BFS可以非常简单地写出来,代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#define num 105*105

int a[105][105];//地图存储
int book[105][105];//地图标记
int next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//移动数组,顺时针

struct note
{
    int x;
    int y;
};

int main()
{
    int n,m,startx,starty,tx,ty,head,tail,sum,k;
    struct note que[num];
    head=1,tail=1;
    scanf("%d%d%d%d",&n,&m,&startx,&starty);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    head=1,tail=1,sum=1;
    que[tail].x=startx,que[tail].y=starty;
    book[startx][starty]=1;
    tail++;
    while(head<tail)
    {
        for(k=0;k<4;k++)
        {
            tx=que[head].x+next[k][0];
            ty=que[head].y+next[k][1];
            if(tx<1||tx>n||ty<1||ty>m)
            {
                continue;
            }
            else if(a[tx][ty]>0&&book[tx][ty]==0)
            {
                book[tx][ty]=1;
                que[tail].x=tx,que[tail].y=ty;
                sum++,tail++;
            }
        }
        head++;
    }
    printf("%d",sum);
    return 0;
}

SCAU 18276 走迷宫

题目描述

18276 走迷宫
时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题 语言: G++;GCC;VC
Description
有一个N*M的格子迷宫,1代表该格子为墙,不能通过,0代表可以通过,另外,在迷宫中
有一些传送门,走到传送门的入口即会自动被传送到传送门的出口(一次传送算1步)。人在迷宫中可以尝试
上下左右四个方向移动。现在给定一个迷宫和所有传送门的出入口,以及起点和终点,
问最少多少步可以走出迷宫。如果不能走出迷宫输出“die”。

输入格式
该程序为多CASE,第1行为CASE的数量
每一个CASE,第1行为两个数N(行)和M(列)
然后N行每行M个数
之后是一个数W,为传送门的数量
之后每行一个传送门的入口坐标c1(行),r1(列)和出口坐标c2,r2
之后是起点坐标和终点坐标sc(行) sr(列) ec(行) er(列)

注:传送门出入口和起点坐标和终点坐标不会出现在墙的位置
所有数字不超过100

输出格式
如题

输入样例
2
4 3
011
011
110
110
1
1 0 2 2
0 0 3 2
2 2
01
10
0
0 0 1 1

输出样例
3
die

问题分析

观察数据范围(100*100),如果用的是dfs的解法,将会超时,那么有两个方案:
一个设定步数数组,提前预判剪枝,可以大幅减少时间复杂度.
采用BFS,最优解法

解法思路

首先这道题由于是有传送点的情况,那么也就是说,在后来队列尾部加入节点的时候,是多了选择的,本来只是上下左右走的,但是有了传送门这个选择,可以导致我们扩展出来的节点不同。那么首先需要做的是,判断该点是不是传送门
使用的是如下函数

int IsTp(int x,int y,int n)/*判断是不是tp点*/
{
    int i;
    for(i=0; i<n; i++)
    {
        if(goin[i][0]==x && goin[i][1]==y)
        {
            return i;
        }
    }
    return -1;
}

然后在我们正常的BFS过程中,这道题有什么不一样呢?
首先,如果队头节点是传送门节点的话,那么扩展出来的节点只会有一个
其次,队头节点是传送门节点的话,扩展出来的节点需要我们手动从数组中调出来
最后,需要特别注意的是,小人的起始点可以是传送门,那么也就意味着,起始点其实是变的,就是说题目给的起始点是一个烟雾弹,起始点其实是当前传送门传送过去的那个点。

AC代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int INF=0x3f3f3f3f;
const int M=100+5;
const int N=1e5+5;
char a[M][M];
int book[M][M],goin[M][2],out[M][2];/*记录迷宫的出口和入口*/

struct node
{
    int x;
    int y;
    int step;
};

int IsTp(int x,int y,int n)/*判断是不是tp点*/
{
    int i;
    for(i=0; i<n; i++)
    {
        if(goin[i][0]==x && goin[i][1]==y)
        {
            return i;
        }
    }
    return -1;
}

int main()
{
    /*BFS*/
    int i,j,startx,starty,targetx,targety,w,n,m;
    bool IsFind=false;
    int dir[4][2]= {{0,1},{1,0},{0,-1},{-1,0}}; /*方向数组*/
    struct node q[N];/*最多会扩展出M个节点*/
    ios::sync_with_stdio(false);
    int kase;
    cin>>kase;
    while(kase--)
    {
        memset(book,0,sizeof(book));
        memset(a,0,sizeof(a));
        memset(out,0,sizeof(out));
        memset(goin,0,sizeof(goin));
        memset(q,0,sizeof(q));
        IsFind=false;
        cin>>n>>m;
        int head=0,tail=0,tx,ty;
        for(i=0; i<n; i++)
        {
            cin>>a[i];/*读入数据*/
        }
        cin>>w;
        for(i=0; i<w; i++)
        {
            for(j=0; j<2; j++)
            {
                cin>>goin[i][j];
            }
            for(j=0; j<2; j++)
            {
                cin>>out[i][j];
            }
        }
        cin>>startx>>starty>>targetx>>targety;
        /*队列初始化*/
        q[tail].x=startx;
        q[tail].y=starty;
        book[startx][starty]=1;
        q[tail].step=0;
        tail++;
        /*首先要检测起始点是不是传送门*/
        if(IsTp(startx,starty,w)!=-1)/*是传送门*/
        {
            int pos=IsTp(startx,starty,w);
            tx=out[pos][0],ty=out[pos][1];
            q[tail].x=tx,q[tail].y=ty;
            q[tail].step=q[head].step+1;
            /*如果传送了,那么上一个点就没用了*/
            head++;
            tail++;
        }
        /*BFS循环*/
        while(head<tail)
        {
            /*应对特殊情况 */
            if(q[head].x==targetx && q[head].y==targety)
            {
                    IsFind=true;
                    tail++;
                    q[tail-1].step=q[head].step;
                    break;
            }
            /*如果头节点只能扩展出到传送门的节点*/
            if(IsTp(q[head].x,q[head].y,w)!=-1)
            {
                int pos=IsTp(q[head].x,q[head].y,w);
                q[tail].x=out[pos][0];
                q[tail].y=out[pos][1];
                q[tail].step=q[head].step+1;
                book[q[tail].x][q[tail].y]=1;
                if(q[tail].x==targetx && q[tail].y==targety)
                {
                    IsFind=true;
                    tail++;
                    break;
                }
                tail++;
                head++;
                continue;
            }
            for(int k=0; k<4; k++)
            {
                tx=q[head].x+dir[k][0];
                ty=q[head].y+dir[k][1];
                if(tx<0||tx>n||ty<0||ty>m)
                {
                    continue;
                }
                if(book[tx][ty]==0 && a[tx][ty]=='0')/*如果可以走动*/
                {
                    q[tail].x=tx,q[tail].y=ty;
                    q[tail].step=q[head].step+1;
                    book[tx][ty]=1;
                    if(tx==targetx&& ty==targety)
                    {
                        IsFind=true;
                    }
                    tail++;
                }
            }
            if(IsFind)
            {
                break;
            }
            head++;
        }
        if(IsFind)
        {
            cout<<q[tail-1].step<<'\n';
        }
        else
        {
            cout<<"die"<<'\n';
        }
    }
    return 0;
}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值