1. 程式人生 > >[USACO2.1]城堡 The Castle

[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;
}