USACO:2.1.1 The Castle 城堡

2.1.1 The Castle 城堡

一、题目描述

The Castle 城堡
        以一个几乎超乎想像的运气,农民约翰在他的生日收到了一张爱尔兰博彩的奖券.这一张奖券成为了唯一中奖的奖券.农民约翰嬴得爱尔兰的乡下地方的一个传说中的城堡.
        吹牛在他们威斯康辛州不算什么,农民约翰想告诉他的牛所有有关城堡的事.他想知道城堡有多少房间,而且最大的房间有多大.事实上,他想去掉一面墙来制造一个更大的房间.
        你的任务是帮助农民约翰去了解正确房间数目和大小.
城堡的平面图被分为 M(wide)*N(1 <=M,N<=50)个小正方形.
每个这样的小正方形有0 到4 面墙.
城堡在它的外部边缘总是有墙壁的,好遮挡风雨.
考虑这注解了一个城堡的平面图:
        1        2       3      4        5       6       7
    #############################
1  #       |        #       |        #        |        |        #
    #####-----#####-----#------#####-----#
2  #      #        |        #       #       #       #       #
    #-----#####------#####-----#####-----#
3  #       |        |         #       #       #       #      #
    #-----#########------#####-----#-----#
4  #  -> #       |         |         |        |        #      #
    #############################
# =墙壁        -, | = 没有墙壁
-> =移除这面墙能使得到的新房间最大.

例子的城堡的大小是7 x 4.
一个 "房间"是平面图上有连接的"小正方形"的集合.
一个 "房间"是平面图上有连接的"小正方形"的集合.
这个平面图包含五个房间.(它们的大小是9,7,3,1, 和 8 排列没有特别的顺序).
移除被箭作记号的墙壁来合并两个房间来制造最大的可能房间(移除一面墙所能产生的).
城堡总是至少有二个房间并且总是有一面墙壁以可能被移除.

PROGRAM NAME:castle
INPUT FORMAT
地图以一个表格来储存,每个数字描述一个小正方形,N 行每行M 个数来描述这个平面图.
输入顺序符合那个在上面例子的编号方式.
每个描述小正方形的数字说明小正方形的四面的墙的分布情况,它是下面4 个数的和:
1: 在西面有墙
2: 在北面有墙
4: 在东面有墙
8: 在南面有墙
内部的墙壁是会被定义两次;小正方形(1,1)南面的墙也被指出是小正方形(2,1)北面的墙.
第 1 行: 二个被空格分开的整数: M 和 N
第 2 到: N+1 行: M x N 个整数,每行M 个.
SAMPLE INPUT (file castle.in)
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
OUTPUT FORMAT
输出包含一些行:
第 1 行: 城堡的房间数目.
第 2 行: 最大的房间的大小
第 3 行: 移除一面墙能得到的最大的房间的大小
第 4 行: 移除哪面墙
选择最佳的墙来移除,(选择最靠西的,如果仍然不能确定,再选择最靠南的.编者注:墙的位置应该
由它的中点来定义)
(【原文】Choose the optimal wall to remove from the set of optimal walls by choosing the wall farther to the west (and then, if still tied, farthest to the south).)
墙壁由它在相邻的小正方形的西部或南方来命名
SAMPLE OUTPUT(file castle.out)
5
9
16
4 1 E

二、解题思路

  floodfill种子染色法(种子填充法):用一个简单的DFS递归搜索,对每一个房间进行进行染色,然后搜索所有能通过推到一面墙来组成一个大房间的房间对,选择其中新形成最大房间所需要推倒的墙。
  1、用一个二维数组存储原始输入数据;根据输入,利用位运算得出每个格的墙的情况(8 = 23,4 = 22,2 = 21,1 = 20)。 然后用floodfill,对每个房间染色,求出房间数和每个房间的面积,输出其中最大的。
  2、选择最佳的墙来推倒。有多解时选(重心,在图中即墙的中点)最靠西的(仍然有多解时选这些里面(重心)最靠南的)。用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位("N"(北)或者"E"(东))。枚举每堵墙具体方法,为了使结果唯一,我们从左下向右上,一列一列枚举格子,并且先枚举该格上面的墙再枚举右边的。如果墙的两边不是同一个房间且其面积和更大,更新结果。(按从西到东(列向量),从南到北(行向量)顺序枚举不会出现这种情况for(int j=1;j<=m;++j)for(int i=n;i>=1;--i))

下面是USACO的参考答案:
<span style="font-size:14px;">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXDIM 50
#define MAXN 100
#define MAXCOLOR 100
#define MAXROOM (MAXDIM*MAXDIM)

enum {
    Wwest = 1,
    Wnorth = 2,
    Weast = 4,
    Wsouth = 8
};

typedef struct Square	Square;
struct Square {
    int wall;
    int numbered;
    int room;
};
 
int wid, ht;
Square castle[MAXDIM][MAXDIM];
int roomsize[MAXROOM];

void //对房间进行染色,把属于同一个房间的格子进行标号
number(int room, int x, int y)
{
    int w;

    if(castle[x][y].numbered) {
	assert(castle[x][y].room == room);
	return;
    }

    castle[x][y].numbered = 1;
    castle[x][y].room = room;
    roomsize[room]++;

    w = castle[x][y].wall;

    if(x > 0 && !(w & Wwest))
	number(room, x-1, y);

    if(x+1 < wid && !(w & Weast))
	number(room, x+1, y);

    if(y > 0 && !(w & Wnorth))
	number(room, x, y-1);

    if(y+1 < ht && !(w & Wsouth))
	number(room, x, y+1);
}

void
main(void)
{
    FILE *fin, *fout;
    int x, y, w, nroom, bigroom;
    int i, n, m, mx, my;
    char mc;

    fin = fopen("castle.in", "r");
    fout = fopen("castle.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d %d", &wid, &ht);

    /* read in wall info */
    for(y=0; y<ht; y++) {
	for(x=0; x<wid; x++) {
	    fscanf(fin, "%d", &w);
	    castle[x][y].wall = w;
	}
    }

    /* number rooms */
    nroom = 0;
    for(y=0; y<ht; y++)
    for(x=0; x<wid; x++)
	if(!castle[x][y].numbered)//判断是否已被标号(访问)
	    number(nroom++, x, y);

    /* find biggest room */
    bigroom = roomsize[0];
    for(i=1; i<nroom; i++)
	if(bigroom < roomsize[i])
	    bigroom = roomsize[i];

    /* look at best that can come of removing an east or north wall */
    m = 0;
    for(x=0; x<wid; x++)
    for(y=ht-1; y>=0; y--) {
	if(y > 0 && castle[x][y].room != castle[x][y-1].room) {
	    n = roomsize[castle[x][y].room] + roomsize[castle[x][y-1].room];
	    if(n > m) {
		m = n;
		mx = x;
		my = y;
		mc = 'N';
	    }
	}
	if(x+1 < wid && castle[x][y].room != castle[x+1][y].room) {
	    n = roomsize[castle[x][y].room] + roomsize[castle[x+1][y].room];
	    if(n > m) {
		m = n;
		mx = x;
		my = y;
		mc = 'E';
	    }
	}
    }

    fprintf(fout, "%d\n", nroom);
    fprintf(fout, "%d\n", bigroom);
    fprintf(fout, "%d\n", m);
    fprintf(fout, "%d %d %c\n", my+1, mx+1, mc);
    exit(0);
}</span>



由于自身是初学者,编程能力有限,未达到专业程序员的水平,可能误导大家,请大家甄读;文字编辑也一般,文中可能会有措辞不当。博文中的错误和不足敬请读者批评指正。

  

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值