The Castle城堡
IOI'94 - Day 1
喜欢吹嘘的农夫约翰立刻回到有着吹嘘传统的威斯康辛老家开始吹嘘了, 农夫约翰想要告诉他的奶牛们关于他城堡的一切。他需要做一些吹嘘前的准备工作:比如说知道城堡有多少个房间,每个房间有多大。另外,农夫约翰想要把一面单独的墙(指两个单位间的墙)拆掉以形成一个更大的房间。 你的工作就是帮农夫约翰做以上的准备,算出房间数与房间的大小。
城堡的平面图被划分成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个房间合为一个新房间,且比移去其他墙所形成的房间都大。(原文为:Removing the wall marked by the arrow merges a pair of rooms to make the largest possible room that can be made by removing a single wall. )
城堡保证至少有2个房间,而且一定有一面墙可以被移走。
PROGRAM NAME: castle
INPUT FORMAT: 第一行有两个整数:M和N 城堡的平面图用一个由数字组成的矩阵表示,一个数字表示一个单位,矩阵有N行M列。输入与样例的图一致。
每一个单位的数字告诉我们这个单位的东西南北是否有墙存在。每个数字是由以下四个整数的某个或某几个或一个都没有加起来的。
1: 在西面有墙
2: 在北面有墙
4: 在东面有墙
8: 在南面有墙
城堡内部的墙会被规定两次。比如说(1,1)南面的墙,亦会被标记为(2,1)北面的墙。
OUTPUT FORMAT:
(file castle.out)
输出包含如下4行:
第 1 行: 城堡的房间数目。
第 2 行: 最大的房间的大小
第 3 行: 移除一面墙能得到的最大的房间的大小
第 4 行: 移除哪面墙可以得到面积最大的新房间。
选择最佳的墙来推倒。有多解时选最靠西的,仍然有多解时选最靠南的。同一格子北边的墙比东边的墙更优先。
用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位("N"(北)或者"E"(东))。
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
5
9
16
4 1 E
floodfill 选择最佳的墙来推倒。有多解时选(重心)最靠西的(仍然有多解时选这些里面(重心)最靠南的)。用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位("N"(北)或者"E"(东))。 (若按从西到东,从南到北顺序枚举不会出现这种情况for(int j=1;j<=m;++j)for(int i=n;i>=1;--i))
“有多解时选(重心)最靠西的(仍然有多解时选这些里面(重心)最靠南的)。” 请注意重心二字! 北墙的重心当然比东墙的要靠西啊~~
用一个二维数组记录每个格的四面是否有墙,根据输入,利用位运算得出每个格的墙的情况(8 = 23,4 = 22,2 = 21,1 = 20)。 然后用floodfill,对每个房间染色,求出房间数,和每个房间的面积,输出其中最大的。
枚举每堵墙,如果墙的两边不是同一个房间且其面积和更大,更新结果,为了结果唯一,我们从左下向右上,一列一列枚举格子,并且先枚举该格上面的墙再枚举右边的。
下面是我的代码(写的长了些)我主要分init、floodfill、work 和pull 来完成主要操作: floodfill就是标记在同一房间的所有房间,用area[]计数, work用来完成不同的房间数和最大的房间数 pull用来完成拆除一边墙后最大的房间,标记坐标和墙,输出 注意: 拆墙时有规则,从下往上,从左往右
/* ID: 138_3531 LANG: C++ TASK: castle */ #include <fstream> #include <iostream> using namespace std; int N,M; int g[51][51][4]; int a[51][51]; int vis[51][51]; //染色 int count; //总共几个房间 int area[2501]; //每个房间面积 int maxarea; int maxarea2,ansi,ansj,ansdir; ifstream fin("castle.in"); ofstream fout("castle.out"); int max(int a,int b) { return a>b?a:b; } void init() { fin>>N>>M; for (int i=0;i<M;i++) for (int j=0;j<N;j++) { fin>>a[i][j]; int n=3; while (n!=-1) { if (a[i][j]-(1<<n)>=0) { g[i][j][n]=1; //记录每个格子哪面有墙 a[i][j]-=1<<n; } n--; } } } void floodfill(int i,int j,int k) //work + floodfill 染色 { if (!vis[i][j]) { vis[i][j]=k; area[k]++; if (!g[i][j][0]) floodfill(i,j-1,k); if (!g[i][j][1]) floodfill(i-1,j,k); if (!g[i][j][2]) floodfill(i,j+1,k); if (!g[i][j][3]) floodfill(i+1,j,k); } } void work() { count=1; for (int i=0;i<M;i++) for (int j=0;j<N;j++) if (!vis[i][j]) { floodfill(i,j,count); maxarea=max(maxarea,area[count]); count++; } } void pull() { maxarea2=maxarea; for (int j=0;j<N;j++) for (int i=M-1;i>=0;i--) { if ((g[i][j][1])&&(i>0)) if (vis[i][j]!=vis[i-1][j]) if (area[vis[i][j]]+area[vis[i-1][j]]>maxarea2) { maxarea2=area[vis[i][j]]+area[vis[i-1][j]]; ansi=i; ansj=j; ansdir=1; } if ((g[i][j][2])&&(j<N-1)) if (vis[i][j]!=vis[i][j+1]) if (area[vis[i][j]]+area[vis[i][j+1]]>maxarea2) { maxarea2=area[vis[i][j]]+area[vis[i][j+1]]; ansi=i; ansj=j; ansdir=2; } } } int main() { init(); work(); pull(); count--; fout<<count<<endl; fout<<maxarea<<endl; fout<<maxarea2<<endl; fout<<ansi+1<<' '<<ansj+1<<' '; if (ansdir==1) fout<<'N'<<endl; else fout<<'E'<<endl; return 0; }