USACO2.1 城堡The Castle

本文介绍了一种算法,用于计算城堡平面图上的房间数量及大小,并找出通过拆除一面墙来合并房间以形成最大单一房间的方法。该算法适用于面积不超过50x50单位的城堡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

城堡

题目描述

我们憨厚的USACO主人公农夫约翰(Farmer John)以无法想象的运气,在他生日那天收到了一份特别的礼物:一张“幸运爱尔兰”(一种彩票)。结果这张彩票让他获得了这次比赛唯一的奖品——坐落于爱尔兰郊外的一座梦幻般的城堡!

喜欢吹嘘的农夫约翰立刻回到有着吹嘘传统的威斯康辛老家开始吹嘘了, 农夫约翰想要告诉他的奶牛们关于他城堡的一切。他需要做一些吹嘘前的准备工作:比如说知道城堡有多少个房间,每个房间有多大。另外,农夫约翰想要把一面单独的墙(指两个单位间的墙)拆掉以形成一个更大的房间。 你的工作就是帮农夫约翰做以上的准备,算出房间数与房间的大小。

城堡的平面图被划分成M*N(1 <=M,N<=50)个正方形的单位,一个这样的单位可以有0到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个房间,而且一定有一面墙可以被移走。

分析:搜索,把属于同一个的点标记,在另一个数组里存每个点所属房间的大小,最后枚举每个点,看拆除一面墙后两个房间合并的最大值是否大于max。

代码

const
  maxn=50;
  dx:array[1..4] of -1..1=(-1,0,1,0);
  dy:array[1..4] of -1..1=(0,1,0,-1);
var
  a:array[0..maxn,0..maxn,1..4] of boolean;
  b,c:array[0..maxn,0..maxn] of longint;
  f:array[0..maxn,0..maxn] of boolean;
  sum:array[0..5000] of longint;
  n,m,i,j,x,max,ans1,ans2,ansx,ansy:longint;
  ansd:char;

function check(x,y,x1,y1,d:longint):boolean;
begin
  check:=true;
  inc(d);
  if d>4 then d:=1;
  if (f[x,y]) or (a[x1,y1,d]) then exit(False);
end;


procedure dfs(x,y:longint);
var
  i:longint;
begin
  for i:=1 to 4 do
    if check(x+dx[i],y+dy[i],x,y,i) then
      begin
        inc(max);
        f[x+dx[i],y+dy[i]]:=true;
        dfs(x+dx[i],y+dy[i]);
      end;
  c[x,y]:=ans2+1;
end;


begin
  readln(m,n);
  for i:=1 to n do
    for j:=1 to m do
      begin
        read(x);
        if x>=8 then begin dec(x,8);a[i,j,4]:=true;a[i+1,j,2]:=true;end;
        if x>=4 then begin dec(x,4);a[i,j,3]:=true;a[i,j+1,1]:=true;end;
        if x>=2 then begin dec(x,2);a[i,j,2]:=true;a[i-1,j,4]:=true;end;
        if x>=1 then begin dec(x);a[i,j,1]:=true;a[i,j-1,3]:=true;end;
      end;
  for i:=1 to n do
    for j:=1 to m do
      if not f[i,j] then
        begin
          max:=1;
          f[i,j]:=true;
          dfs(i,j);
          inc(ans2);
          sum[ans2]:=max;
          if max>ans1 then ans1:=max;
        end;
  writeln(ans2);
  writeln(ans1);
  max:=0;
  for i:=1 to n do
    for j:=1 to m do
      b[i,j]:=sum[c[i,j]];
  for i:=1 to m do
    for j:=n downto 1 do
      begin
        if a[j,i,2] then
          if (b[j,i]+b[j-1,i]>max) and (j<>1) and (c[j,i]<>c[j-1,i])then
            begin
              max:=b[j,i]+b[j-1,i];
              ansd:='N';
              ansx:=j;
              ansy:=i;
            end;
        if a[j,i,3] then
          if (b[j,i]+b[j,i+1]>max) and (i<>m) and (c[j,i]<>c[j,i+1]) then
            begin
              max:=b[j,i]+b[j,i+1];
              ansd:='E';
              ansx:=j;
              ansy:=i;
            end;
      end;
  writeln(max);
  writeln(ansx,' ',ansy,' ',ansd);


end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值