【广度优先搜索】ybt1250 The Castle

摘要:
Pid=1250[主题描述]城堡分为m*n个区块(m≤50,每个整数代表该区块在平面图中相应位置的特征。每个区块中的墙的特征由数字P(0≤P≤15)描述。Unit()//必要的初始化操作{x=0,vis=false;memset(wall,sizeof(wall));}Unit(intx,for(int i=0;num)wall[i]=false;elseif(num&

http://ybt.ssoier.cn:8088/problem_show.php?pid=1250

【题目描述】

一座城堡被分成m*n个方块(m≤50,n≤50),每个方块可有0~4堵墙(0表示无墙)。下面示出了建筑平面图:

【广度优先搜索】ybt1250 The Castle第1张

图中的加粗黑线代表墙。几个连通的方块组成房间,房间与房间之间一定是用黑线(墙)隔开的。

现在要求你编一个程序,解决以下2个问题:

1、该城堡中有多少个房间?

2、最大的房间有多大?

【输入】

平面图用一个数字表示一个方块(第1个房间用二进制1011表示,0表示无东墙,用十进制11表示)。

第一行一个整数m(m≤50),表示房子南北方向的长度。

第二行一个整数n(n≤50),表示房子东西方向的长度。

后面的m行,每行有n个整数,每个整数都表示平面图对应位置的方块的特征。每个方块中墙的特征由数字P来描述(0≤P≤15)。数字P是下面的可能取的数字之和:

1(西墙 west)

2(北墙 north)

4(东墙 east)

8(南墙 south)

室内的墙被定义两次: 例如方块(1,1)中的南墙也被位于其南面的方块(2,1)定义了一次。

建筑中至少有两个房间。

【输出】

第1行:一个整数,表示房间总数;

第2行:一个整数,表示最大房间的面积(方块数)。

一道很好的广搜题目,竟然和二进制结合在了一起。

第一次看到这道题怎么着也是两月以前了。当时一看到这道题我的确不想做,我一开始的思路是既存储墙壁又存储房间(这里指单位房间而不是连成一体的房间),然后根据房间和墙壁的相对位置判断 bfs 是否能执行。但是很明显,一行数组不能拆成两半(一半存储房间,一半存储墙壁),也就是说必须开一个 2M * 2N 的数组,而每次扩展新结点时都要移动两个格子,这无疑给程序设计造成了很大的麻烦。

时隔两月,我又开始思考这个题目。突然,我灵机一动:为什么非要存储墙壁?只要设计一个结构体表示每个单位房间,然后在这个房间里实现墙壁的存储不就是了!

构建结构体对象时,根据输入的数字,将一个布尔数组 wall[4] 填充,其中 wall[0]为west、wall[1]为north、wall[2]为east、wall[3]为south。true表示有某个方向有墙壁,false表示没有。

然后是这奇怪的墙壁表示方法。很有意思,这里采用了二进制。每一个输入的数字都可以看做一个位数不超过 4 的二进制数。只要每次实行位或和位移位操作,就可以知道哪些方向存在墙壁。

同时还要记录单位房间的位置和是否被访问过。这就是结构体的好处:具有高度的归纳性和整合性。我们不需要再额外地开一个数组来表示某个房间有没有被访问过,从而降低了代码的复杂度(这里的复杂度是指代码的糟乱程度,而不是时(空)间复杂度)。

然后是构造函数,每次输入时根据要求处理每一个结构体对象。

struct unit
{
    int x,y;
    bool wall[4];//1=w,2=n,3=e,4=s;
    bool vis;
    unit()//必要的初始化操作
    {
        x=0,y=0,vis=false;
        memset(wall,false,sizeof(wall));
    }
    unit(int x,int y,int num)
    {
        this->x=x;
        this->y=y;
        for(int i=0;i<4;i++)
        {
            if(!num) wall[i]=false;
            else if(num & 1)//每次实行异或,判断当前位数是否为1或0,分别代表墙壁的存在和不存在。注意要按照指定的西北东南的顺序。
            {
                wall[i]=true;
            }
            else wall[i]=false;
            num=num>>1;//下一位
        }
    }    
}a[55][55];//对于数组,如果我们不编写默认构造函数unit(),那么我们就得一个个手动调用unit(x,y,num)函数,否则会发生错误

然后就可以初始化:

void init()
{
    cin>>M>>N;
    int temp;
    for(int i=1;i<=M;i++)
    {
        for(int j=1;j<=N;j++)
        {
            cin>>temp;
            a[i][j]=unit(i,j,temp);//创建新对象的另一个方法
        }
    }
}

最后是重中之重的广搜函数。这里需要注意几点:

1.wall[4]的下标一定要和西北东南对应起来,还有表示扩展结点方向的数组dx[4],dy[4]也要严格按照这样的顺序。也就是说我们不能随便地给dx、dy数组赋值,必须有一定的顺序。比如说往西走是(x+0,y-1),则这一对必须要在下标 0 上。

2.注意更新最大值和房间量。

3.判断一个结点是否有效时,除了判断下标范围是否在M、N以内且大于0以外,还要判断一个单位房间是否走过。此时判断“是否存在墙壁”使用当前对象下标(也就是刚从队列里pop掉的对象),而判断有没有走过则是使用当前对象扩展的下一个对象的下标。

不理解可以看代码:

int M,N;
int dx[4]={0,-1,0,1};//注意点1
int dy[4]={-1,0,1,0};
int maxsize=-1;
int count;
void bfs(int x,int y)
{
    if(a[x][y].vis) return;
    int Size=1;
    queue<unit> q;
    q.push(a[x][y]);
    a[x][y].vis=true;
    while(!q.empty())
    {
        unit u=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int nx=u.x+dx[i],ny=u.y+dy[i];
            if(nx>=1 && nx<=M && ny>=1 && ny<=N && (!u.wall[i]) && (!a[nx][ny].vis))//注意3:不是“!u.vis”!
            {
                q.push(a[nx][ny]);
                a[nx][ny].vis=true;
                Size++;
            } 
        }
    }
    maxsize=max(Size,maxsize);//注意点2
    count++;
}

AC代码:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int M,N,dx[4]={0,-1,0,1},dy[4]={-1,0,1,0},maxsize=-1,count;
struct unit
{
    int x,y;
    bool wall[4];//1=w,2=n,3=e,4=s;
    bool vis;
    unit()
    {
        x=0,y=0,vis=false;
        memset(wall,false,sizeof(wall));
    }
    unit(int x,int y,int num)
    {
        this->x=x;
        this->y=y;
        for(int i=0;i<4;i++)
        {
            if(!num) wall[i]=false;
            else if(num & 1)
            {
                wall[i]=true;
            }
            else wall[i]=false;
            num=num>>1;
        }
    }    
}a[55][55];
void init()
{
    cin>>M>>N;
    int temp;
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
        {
            cin>>temp;
            a[i][j]=unit(i,j,temp);
        }
}
void bfs(int x,int y)
{
    if(a[x][y].vis) return;
    int Size=1;
    queue<unit> q;
    q.push(a[x][y]);
    a[x][y].vis=true;
    while(!q.empty())
    {
        unit u=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int nx=u.x+dx[i],ny=u.y+dy[i];
            if(nx>=1 && nx<=M && ny>=1 && ny<=N && (!u.wall[i]) && (!a[nx][ny].vis))
            {
                q.push(a[nx][ny]);
                a[nx][ny].vis=true;
                Size++;
            } 
        }
    }
    maxsize=max(Size,maxsize);
    count++;
}
int main()
{
    init();
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
            bfs(i,j);
    cout<<count<<endl<<maxsize;
    return 0;
}

免责声明:文章转载自《【广度优先搜索】ybt1250 The Castle》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Maven中常见的问题Visual Studio 智能提示功能消失解决办法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

GCD与莫比乌斯反演的勾当

目录 机房最后一个学懵逼钨丝的人 题目一 链接 式子 注意 代码 题目 链接&&问题 公式 代码 bzoj1101 链接 式子 代码 机房最后一个学懵逼钨丝的人 题目一 链接 题目没找到 求(sum_{1}^{n}sum_{1}^{m}gcd(i,j)) 式子 (sumlimits_{i=1}^{N}suml...

pipenv@python3.8 install tensorflow

    普通的pipenv install pipenv install tensorflow Courtesy Notice: Pipenv found itself running within a virtual environment, so it will automatically use that environme...

朴素贝叶斯算法(python)

朴素贝叶斯算法 优点: 算法原理和实现简单,常用于文本分类。 对小规模数据表现很好,适合多分类增量式训练任务。 对缺失数据不太敏感。 缺点: 对输入数据的表达形式很敏感 需要计算先验概率,分类决策存在错误率 要求样本之间相互独立,这就是“朴素”的意思,这个限制有时很难做到,或使用者误以为符合而造成错误的结果 适用数据类型:离散型数据。 朴素贝叶斯假...

学习如何高效率编写单片机代码,优化程序设计

1、 使用尽量小的数据类型 能用unsiged就不用signed;能用char就不用int;能不用floating就不用;能用位操作不用算数。 2、使用自加、自减指令 通常使用自加、自减指令和复合赋值表达式(如a-=1 及a+=1 等)都能够生成高质量的程序代码,编译器通常都能够生成inc 和dec 之类的指令,而使用a=a+1 或a=a-1 之类的指令,...

30种下载Youtube视频的方法

在线下载Youtube视频1. ClipnabberClipnabber可以下载包括Youtube在内的几乎所有视频网站的视频,包括优酷、土豆等等。用户只需复制想要下载的视频的URL网址,点击“Nab Video”即可下载视频并重命名文件,格式为FLV。另外,Clipnabber还提供了下载视频的便捷书签。<推荐>2. Zam...

jquery json 格式教程

介绍我们知道AJAX技术能够使得每一次请求更加迅捷,对于每一次请求返回的不是整个页面,也仅仅是所需要返回的数据。通常AJAX通过返回XML格式的数据,然后再通过客户端复杂的JavaScript脚本解析和渲染这些XML格式的数据。JSON(读Jason)是为了能够使得数据格式成为一种标准,更简单的被JavaScript解析。 优点1、轻量级的数据交换...