【洛谷1457 [usaco 2.1]城堡 The Castle】【深搜】

4 篇文章 0 订阅
2 篇文章 0 订阅

原题地址:http://www.luogu.org/problem/show?pid=1457

题目描述

我们憨厚的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个房间,而且一定有一面墙可以被移走。

输入输出格式

输入格式:
第一行有两个整数:M和N 城堡的平面图用一个由数字组成的矩阵表示,一个数字表示一个单位,矩阵有N行M列。输入与样例的图一致。

每一个单位的数字告诉我们这个单位的东西南北是否有墙存在。每个数字是由以下四个整数的某个或某几个或一个都没有加起来的。

1: 在西面有墙

2: 在北面有墙

4: 在东面有墙

8: 在南面有墙

城堡内部的墙会被规定两次。比如说(1,1)南面的墙,亦会被标记为(2,1)北面的墙。

输出格式:
输出包含如下4行:

第 1 行: 城堡的房间数目。

第 2 行: 最大的房间的大小

第 3 行: 移除一面墙能得到的最大的房间的大小

第 4 行: 移除哪面墙可以得到面积最大的新房间。

选择最佳的墙来推倒。有多解时选最靠西的,仍然有多解时选最靠南的。同一格子北边的墙比东边的墙更优先。

/*
ID: ylx14271
PROG: castle
LANG: C++     
*/
#include<set>  
#include<map>  
#include<list>  
#include<queue>  
#include<stack>  
#include<string>  
#include<math.h>  
#include<time.h>  
#include<vector>  
#include<bitset>  
#include<memory>  
#include<utility>  
#include<stdio.h>  
#include<sstream>  
#include<iostream>  
#include<stdlib.h>  
#include<string.h>  
#include<algorithm> 
#define LL unsigned long long   
using namespace std;
int f[51][51][5];//存墙,f[i][j][0]存所属标号 
int n,m,i,j,ii,jj;
int p=0;//连通块的个数 
int a[2501];//存每个连通块的大小? 
int ff[5][3]={{0,0,0},
             {3,0,1},//右 
             {4,1,0},//下 
             {1,0,-1},//左 
             {2,-1,0}};//上,晕了~~~~(>_<)~~~~  
//上下左右四个方向,f[i][0]存到达那需穿过的墙的方向 
void dfs(int x,int y,int id)//dfs求连通块,id是标记的编号 
{
    if (x<=0||x>n||y<=0||y>m) return;//越界辣~\(≧▽≦)/~
    if (f[x][y][0]!=0) return;//被搜过辣,当然这种情况是不会出现的 
    f[x][y][0]=id;//存编号 
    a[id]++; 
    for (int ii=1;ii<=4;ii++)//四种情况去搜
        if ((f[x+ff[ii][1]][y+ff[ii][2]][0]==0)/*没被搜过*/&&(f[x][y][ff[ii][0]]==0)/*过去没墙*/)
        dfs(x+ff[ii][1],y+ff[ii][2],id);//往下搜 
}
int main() 
{
    freopen("castle.in","r",stdin);
    freopen("castle.out","w",stdout); 
    scanf("%d%d",&m,&n);//读入;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
        {
            int hehe;
            scanf("%d",&hehe);//读入墙的数字和
            if (hehe>=8) 
            {
                hehe-=8; 
                f[i][j][4]=1;//标记南方有墙 
            }
            if (hehe>=4) 
            {
                hehe-=4;
                f[i][j][3]=1;//标记东方有墙 
            } 
            if (hehe>=2) 
            {
                hehe-=2;
                f[i][j][2]=1;//标记北方有墙 
            } 
            if (hehe>=1) 
            {
                hehe-=1;
                f[i][j][1]=1;//标记西方有墙 
            }  
            //终于标记完了╮(╯▽╰)╭
            //这种方法好巧啊。 
        }
    for (i=1;i<=n;i++) 
        for (j=1;j<=m;j++)
        {
            if (f[i][j][0]==0)/*目前不属于任何一个连通块*/
            {
                p++;
                dfs(i,j,p);//++p是目前搜的这个连通块的标记 
            }
        }
    printf("%d\n",p);//成功灭掉第一问 
    int maxx=a[1];//初始化 
    for (i=1;i<=p;i++) if (a[i]>maxx) maxx=a[i];
    printf("%d\n",maxx); //成功灭掉第二问 
    //由题可知,移去的墙相对于某一个位置来说一定是东面或者北面 
    char k;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++) //枚举 挖走动墙
        {
            if (i!=1&&f[i][j][2]==1&&f[i][j][0]!=f[i-1][j][0])//第一行的北墙不能挖走,而且要有墙
            //而且没有在同一个连通块 
            {
                int hehe=a[f[i][j][0]]+a[f[i-1][j][0]];//合并后连通块大小 
                if (hehe>maxx) 
                {
                    maxx=hehe;
                    ii=i;
                    jj=j;//标记 
                    k='N';//标记 
                }
                if (hehe==maxx)
                {
                    if ((j<jj)/*更西*/||(i>ii&&j<=jj)/*更南且不会更东*/)
                    {
                        ii=i;
                        jj=j;//标记 
                        k='N';//标记 
                    }
                }
            } 
            if (j!=m&&f[i][j][3]==1&&f[i][j][0]!=f[i][j+1][0])//最后一列的东墙不能挖走 ,而且要有墙
            //而且没有在同一个连通块 
            {
                int hehe=a[f[i][j][0]]+a[f[i][j+1][0]];//合并后连通块大小 
                if (hehe>maxx) 
                {
                    maxx=hehe;
                    ii=i;
                    jj=j;//标记 
                    k='E';//标记 
                }
                if (hehe==maxx)
                {
                    if ((j<jj)||(i>ii&&j<=jj))//更西方,更南方且不会更东方 
                    {
                        ii=i;
                        jj=j;//标记 
                        k='E';//标记 
                    }
                }
            } 
        } 
    printf("%d\n",maxx);//第三问完成 
    printf("%d %d %c\n",ii,jj,k);//终于完成了 
    return 0;
}

用该墙的南邻单位的北墙或西邻单位的东墙来表示这面墙,方法是输出邻近单位的行数、列数和墙的方位(”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

说明

USACO 2.1

翻译来自NOCOW

题解

暴力出奇迹

第一,二问:

简单的floodfill。

第三,四问:

“有多解时选(重心)最靠西的(仍然有多解时选这些里面(重心)最靠南的)。” 请注意重心二字! 北墙的重心比东墙的要靠西啊~~所以拆墙顺序是西墙,南墙,北墙,东墙。




/*
ID: ylx14271
PROG: castle
LANG: C++     
*/
#include<set>  
#include<map>  
#include<list>  
#include<queue>  
#include<stack>  
#include<string>  
#include<math.h>  
#include<time.h>  
#include<vector>  
#include<bitset>  
#include<memory>  
#include<utility>  
#include<stdio.h>  
#include<sstream>  
#include<iostream>  
#include<stdlib.h>  
#include<string.h>  
#include<algorithm> 
#define LL unsigned long long   
using namespace std;
int f[51][51][5];//存墙,f[i][j][0]存所属标号 
int n,m,i,j,ii,jj;
int p=0;//连通块的个数 
int a[2501];//存每个连通块的大小? 
int ff[5][3]={{0,0,0},
             {3,0,1},//右 
             {4,1,0},//下 
             {1,0,-1},//左 
             {2,-1,0}};//上,晕了~~~~(>_<)~~~~  
//上下左右四个方向,f[i][0]存到达那需穿过的墙的方向 
void dfs(int x,int y,int id)//dfs求连通块,id是标记的编号 
{
    if (x<=0||x>n||y<=0||y>m) return;//越界辣~\(≧▽≦)/~
    if (f[x][y][0]!=0) return;//被搜过辣,当然这种情况是不会出现的 
    f[x][y][0]=id;//存编号 
    a[id]++; 
    for (int ii=1;ii<=4;ii++)//四种情况去搜
        if ((f[x+ff[ii][1]][y+ff[ii][2]][0]==0)/*没被搜过*/&&(f[x][y][ff[ii][0]]==0)/*过去没墙*/)
        dfs(x+ff[ii][1],y+ff[ii][2],id);//往下搜 
}
int main() 
{
    freopen("castle.in","r",stdin);
    freopen("castle.out","w",stdout); 
    scanf("%d%d",&m,&n);//读入;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
        {
            int hehe;
            scanf("%d",&hehe);//读入墙的数字和
            if (hehe>=8) 
            {
                hehe-=8; 
                f[i][j][4]=1;//标记南方有墙 
            }
            if (hehe>=4) 
            {
                hehe-=4;
                f[i][j][3]=1;//标记东方有墙 
            } 
            if (hehe>=2) 
            {
                hehe-=2;
                f[i][j][2]=1;//标记北方有墙 
            } 
            if (hehe>=1) 
            {
                hehe-=1;
                f[i][j][1]=1;//标记西方有墙 
            }  
            //终于标记完了╮(╯▽╰)╭
            //这种方法好巧啊。 
        }
    for (i=1;i<=n;i++) 
        for (j=1;j<=m;j++)
        {
            if (f[i][j][0]==0)/*目前不属于任何一个连通块*/
            {
                p++;
                dfs(i,j,p);//++p是目前搜的这个连通块的标记 
            }
        }
    printf("%d\n",p);//成功灭掉第一问 
    int maxx=a[1];//初始化 
    for (i=1;i<=p;i++) if (a[i]>maxx) maxx=a[i];
    printf("%d\n",maxx); //成功灭掉第二问 
    //由题可知,移去的墙相对于某一个位置来说一定是东面或者北面 
    char k;
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++) //枚举 挖走动墙
        {
            if (i!=1&&f[i][j][2]==1&&f[i][j][0]!=f[i-1][j][0])//第一行的北墙不能挖走,而且要有墙
            //而且没有在同一个连通块 
            {
                int hehe=a[f[i][j][0]]+a[f[i-1][j][0]];//合并后连通块大小 
                if (hehe>maxx) 
                {
                    maxx=hehe;
                    ii=i;
                    jj=j;//标记 
                    k='N';//标记 
                }
                if (hehe==maxx)
                {
                    if ((j<jj)/*更西*/||(i>ii&&j<=jj)/*更南且不会更东*/)
                    {
                        ii=i;
                        jj=j;//标记 
                        k='N';//标记 
                    }
                }
            } 
            if (j!=m&&f[i][j][3]==1&&f[i][j][0]!=f[i][j+1][0])//最后一列的东墙不能挖走 ,而且要有墙
            //而且没有在同一个连通块 
            {
                int hehe=a[f[i][j][0]]+a[f[i][j+1][0]];//合并后连通块大小 
                if (hehe>maxx) 
                {
                    maxx=hehe;
                    ii=i;
                    jj=j;//标记 
                    k='E';//标记 
                }
                if (hehe==maxx)
                {
                    if ((j<jj)||(i>ii&&j<=jj))//更西方,更南方且不会更东方 
                    {
                        ii=i;
                        jj=j;//标记 
                        k='E';//标记 
                    }
                }
            } 
        } 
    printf("%d\n",maxx);//第三问完成 
    printf("%d %d %c\n",ii,jj,k);//终于完成了 
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值