题目大意
我们憨厚的USACO主人公农夫约翰(Farmer John)以无法想象的运气,在他生日那天收到了一份特别的礼物:一张“幸运爱尔兰”(一种彩票)。结果这张彩票让他获得了这次比赛唯一的奖品——坐落于爱尔兰郊外的一座梦幻般的城堡!
喜欢吹嘘的农夫约翰立刻回到有着吹嘘传统的威斯康辛老家开始吹嘘了, 农夫约翰想要告诉他的奶牛们关于他城堡的一切。他需要做一些吹嘘前的准备工作:比如说知道城堡有多少个房间,每个房间有多大。另外,农夫约翰想要把一面单独的墙(指两个单位间的墙)拆掉以形成一个更大的房间。 你的工作就是帮农夫约翰做以上的准备,算出房间数与房间的大小。
城堡的平面图被划分成M*N(1 <=M,N<=50)个正方形的单位,一个这样的单位可以有0到4面墙环绕。城堡周围一定有外墙环绕以遮风挡雨。(就是说平面图的四周一定是墙。)请仔细研究下面这个有注解的城堡平面图:
1 2 3 4 5 6 7
#############################
1 # | # | # | | #
#####---#####---#---#####---#
2 # # | # # # # #
#---#####---#####---#####---#
3 # | | # # # # #
#---#########---#####---#---#
4 # -># | | | | # #
#############################
# =墙壁 -,| = 没有墙壁
-> =指向一面墙,这面墙推掉的话我们就有一间最大的新房间
友情提示,这个城堡的平面图是7×4个单位的。一个“房间”的是平面图中一个由“#”、“-”、“|”围成的格子(就是图里面的那一个个的格子)。比如说这个样例就有5个房间。(大小分别为9、7、3、1、8个单位(排名不分先后))
移去箭头所指的那面墙,可以使2个房间合为一个新房间,且比移去其他墙所形成的房间都大。
城堡保证至少有2个房间,而且一定有一面墙可以被移走。
格式
INPUT FORMAT:
第一行有两个整数:M和N 城堡的平面图用一个由数字组成的矩阵表示,一个数字表示一个单位,矩阵有N行M列。输入与样例的图一致。
每一个单位的数字告诉我们这个单位的东西南北是否有墙存在。每个数字是由以下四个整数的某个或某几个加起来的(四面都没有墙的话,这个数字应该为0)。
1: 在西面有墙
2: 在北面有墙
4: 在东面有墙
8: 在南面有墙
城堡内部的墙会被规定两次。比如说(1,1)南面的墙,亦会被标记为(2,1)北面的墙。
OUTPUT FORMAT:
输出包含如下4行:
第 1 行: 城堡的房间数目。
第 2 行: 最大的房间的大小
第 3 行: 移除一面墙能得到的最大的房间的大小
第 4 行: 移除哪面墙可以得到面积最大的新房间。
选择最佳的墙来推倒。有多解时选最靠西的,仍然有多解时选最靠南的。同一格子北边的墙比东边的墙更优先。
用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位(”N”(北)或者”E”(东))。
SAMPLE INPUT
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
SAMPLE OUTPUT
5
9
16
4 1 E
(copy from nocow)
题解
这题可以分为两个步骤:
1.搜索(也可以说是用深搜实现的Flood Fill 算法),找到所有的房间,并编上号,记录每个房间的大小。
2.拆墙。枚举所有的正方形格子,如果这个格子的上有一面墙分隔开了两个不同的房间,则记录这面墙。找出所有这样的可以拆的墙,拆最优的那一个。注意拆墙的顺序,从西到东,从南至北。这题给出的数据格式也是很巧秒的,可以利用二进制的位运算简化代码。
1: 在西面有墙
2: 在北面有墙
4: 在东面有墙
8: 在南面有墙
所以每个正方形格子里的数字范围为0~15, 可以用4位的二进制数表示。1 = 20 , 2 = 21 , 4 = 22 , 8 = 23 , 刚好和上面的四种墙对应。判断某一个格子的四个方向有没有墙,只需要判断对应的二进制位上的数字是0还是1就可以了。
我的代码里 dfs()搜索和rmWall()拆墙,都用了位运算使得代码稍微简洁一些。
代码
#include <iostream>
#include <fstream>
#include <cstring>
#define MAXN 51
#define cin fin
#define cout fout
using namespace std;
ifstream fin("castle.in");
ofstream fout("castle.out");
int N, M;
int a[MAXN][MAXN];
int size[MAXN*MAXN], num = 0;
int room[MAXN][MAXN];
int fx[] = {0, -1, 0, 1};
int fy[] = {-1, 0, 1, 0};
char dir[] = {'W', 'N', 'E', 'S'};
void dfs(int x, int y, int n) {
room[x][y] = n;
size[n]++;
int bit = 1;
//位运算 枚举4个方向
for (int i = 0; i < 4; i++) {
if (!(bit & a[x][y])) {
int tx = x + fx[i];
int ty = y + fy[i];
if (tx >= 0 && tx < N &&
ty >= 0 && ty < M &&
!room[tx][ty]) {
dfs(tx, ty, n);
}
}
bit <<= 1;
}
}
int rmWall(int &x, int &y, char &op) {
int tmp = 0;
//枚举所有的格子,注意枚举的方向,
for (int j = 0; j < M; j++) { //从西到东
for (int i = N-1; i >= 0; i--) { //从南至北
int bit = 2; //从2开始,因为西墙就是西边相邻格子的东墙,考虑过了
for (int l = 1; l < 3; l++) {
if (bit & a[i][j]) {
int tx = i + fx[l];
int ty = j + fy[l];
if (tx >= 0 && tx < N &&
ty >= 0 && ty < M &&
room[i][j] != room[tx][ty]) {
if (tmp < size[room[i][j]] + size[room[tx][ty]]) {
tmp = size[room[i][j]] + size[room[tx][ty]];
x = i; y = j; op = dir[l];
}
}
}
bit <<= 1;
}
}
}
return tmp;
}
int main() {
cin >> M >> N;
memset(size, 0, sizeof(size));
memset(a, 0, sizeof(a));
memset(room, 0, sizeof(room));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> a[i][j];
}
}
//搜索
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!room[i][j]) {
++num;
dfs(i, j, num);
}
}
}
//输出房间数和最大房间的大小
cout << num << endl;
int tmp = size[1];
for (int i = 2; i <= num ; i++) {
tmp = max(tmp, size[i]);
}
cout << tmp << endl;
//拆墙
int i, j;
char op;
int ans = rmWall(i, j, op);
cout << ans << endl;
cout << i+1 << " " << j+1 << " " << op << endl;
return 0;
}