[USACO2.1]城堡 The Castle

[USACO2.1]The Castle

题目描述

城堡有多少个房间,每个房间有多大。要把一面单独的墙(指两个单位间的墙)拆掉以形成一个更大的房间。 你的工作就是算出房间数与房间的大小。
城堡的平面图被划分成M*N(1 <=M,N<=50)个正方形的单位,一个这样的单位可以有0到4面墙环绕。城堡周围一定有外墙。(就是说平面图的四周一定是墙。)
请仔细研究下面这个有注解的城堡平面图:

友情提示,这个城堡的平面图是7×4个单位的。一个“房间”的是平面图中一个由“#”、“-”、“|”围成的格子(就是图里面的那一个个的格子)。比如说这个样例就有5个房间。(大小分别为9、7、3、1、8个单位(排名不分先后))
在这里插入图片描述
移去箭头所指的那面墙,可以使2个房间合为一个新房间,且比移去其他墙所形成的房间都大。

城堡保证至少有2个房间,而且一定有一面墙可以被移走。

输入输出格式

输入格式:

第一行有两个整数:M和N 城堡的平面图用一个由数字组成的矩阵表示,一个数字表示一个单位,矩阵有N行M列。输入与样例的图一致。

每一个单位的数字告诉我们这个单位的东西南北是否有墙存在。每个数字是由以下四个整数的某个或某几个或一个都没有加起来的。

1: 在西面有墙

2: 在北面有墙

4: 在东面有墙

8: 在南面有墙

输出格式:

输出格式:

第 1 行: 城堡的房间数目。

第 2 行: 最大的房间的大小

第 3 行: 移除一面墙能得到的最大的房间的大小

第 4 行: 移除哪面墙可以得到面积最大的新房间。

选择最佳的墙来推倒。有多解时选最靠西的,仍然有多解时选最靠南的。同一格子北边的墙比东边的墙更优先。

用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位(“N”(北)或者"E"(东))。

输入输出样例

输入样例#1

7 4
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13

输出样例#1:

5
9
16
4 1 E

题目解析

转换一下,这就是一个求连通块的问题

首先,要处理数据,把数字转换为墙

每个不属于任何一个连通块的点进行宽搜,形成新的连通块,标号并求大小

输出连通块数量和最大的房间

从图的左下角到右上角枚举每个点的墙
推倒墙后与哪个连通块相连,两个连通块相加,求最大值

代码

#include<bits/stdc++.h> 
using namespace std;
struct A
{
    bool a[5];
}mapa[55][55];
bool flag[55][55],bj[55][55];
int n,m,x,num,ansa,ansb,x1,x2;
char c;
int mapb[55][55],siz[2505];
//mapb表示(i,j)点处在哪个连通块    siz表示第i个连通块的大小 
int wa[4]={8,4,2,1},fx[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//四个方向 
queue<int> q;
int bfs()
{
    int x,y,xx,yy,ns=0;
    while(!q.empty())
    {
      x=q.front();q.pop();
      y=q.front();q.pop();
      ns++;
      mapb[x][y]=num;
      flag[x][y]=1;
      for(int i=0;i<4;i++)
       if(!mapa[x][y].a[i])//判断该方向是否有墙 
       {
         xx=x+fx[i][0];yy=y+fx[i][1];
         if(!flag[xx][yy]&&!bj[xx][yy])
         {
         	q.push(xx);
         	q.push(yy);
         	bj[xx][yy]=1;
         }
       }
    }//宽搜
    return ns;//返回该连通块的大小 
}
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
     {
       cin>>x;
       for(int k=0;k<4;k++)
        if(x>=wa[k])
         mapa[i][j].a[k]=1,x-=wa[k];
     }//处理数据,转化为墙 
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      if(!flag[i][j])//判断第(i,j)个点不属于任何一个已知的连通块 
      {
      	num++;//即将求的连通块的编号 
      	q.push(i);q.push(j);
      	siz[num]=bfs();
      	ansa=max(ansa,siz[num]);//求最大值 
      }//每个点进行宽搜 
    cout<<num<<endl<<ansa<<endl;
    int ss=0;
    for(int i=1;i<=m;i++)
     for(int j=n;j>=0;j--)//枚举点,从左下角到右上角 
     {
       //方向的优先级不同 
       if(mapa[j][i].a[3]&&mapb[j][i]!=mapb[j][i-1])
       {
       	 ss=siz[mapb[j][i]]+siz[mapb[j][i-1]];
       	 if(ss>ansb)
         {
           ansb=ss;
           x1=j;x2=i;
           c='W';
         }
       }//推当前点的西墙 
       if(mapa[j][i].a[0]&&mapb[j][i]!=mapb[j+1][i])
       {
       	 ss=siz[mapb[j][i]]+siz[mapb[j+1][i]];
       	 if(ss>ansb)
         {
           ansb=ss;
           x1=j;x2=i;
           c='S';
         }
       }//推当前点的南墙
       if(mapa[j][i].a[2]&&mapb[j][i]!=mapb[j-1][i])
       {
       	 ss=siz[mapb[j][i]]+siz[mapb[j-1][i]];
       	 if(ss>ansb)
         {
           ansb=ss;
           x1=j;x2=i;
           c='N';
         }
       }//推当前点的北墙
       if(mapa[j][i].a[1]&&mapb[j][i]!=mapb[j][i+1])
       {
       	 ss=siz[mapb[j][i]]+siz[mapb[j][i+1]];
       	 if(ss>ansb)
         {
           ansb=ss;
           x1=j;x2=i;
           c='E';
         }
       }//推当前点的东墙
     }
    cout<<ansb<<endl<<x1<<" "<<x2<<" "<<c;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43909855/article/details/85343687
今日推荐