USACO 2.1-The Castle

/*
ID: m1590291
TASK: castle
LANG: C++
*/
#include <iostream>
#include <string.h>
#include <fstream>
using namespace std;
/******************************************************************************************************************
                是时候安利一波了,第一次做图论的题。用USACO上面的话说就是:Bigger Challenges
                思路:
                 1,  直接将输入转化为一个图,找联通块,然后输出最大的。
                 2,  判断两个联通块之间是否相邻,若是,则将他们合并的方式和合并后的尺寸与当前最大值相比较,
                 3,  选取较大的,打印,完事。
                代码思路:
                 1,  把每个格点都抽象为一个顶点,建图,然后进行连通分量的测试。
                 2,  直接用DFS更为简单,对每一个没有检查过的格点进行深搜,
                 3,  给每一个连通分量标记一个独立的标号(房间号),并记下每间房间的大小。
******************************************************************************************************************/
int roomN=0;      //房间数目
int room[55][55];       //每个空间所属房间号。(room[1][1] = 1 : 空间room[1][1] 属于编号为1的房间)
int roomSize[51*51+1];      //每个房间的大小
int Msize=0;      //最大房间大小
int wall[51][51][4];        //每个空间的墙壁状态。 值为1 表示有墙壁,0表示没墙壁。
                            //wall[i][j][0]:西 wall[i][j][1]:北 wall[i][j][2]:东 wall[i][j][3]:南
int f[51][51];      //检查是否涂灰

void dfs(int x,int y)
{
    if(room[x][y] == roomN) return ;

    roomSize[roomN] ++;     //大小++
    f[x][y]=1;
    room[x][y]=roomN;       //更新空间,找到所属房间编号

    if(!wall[x][y][0])   dfs(x,y-1);        //哪面没墙就 dfs
    if(!wall[x][y][1])   dfs(x-1,y);
    if(!wall[x][y][2])   dfs(x,y+1);
    if(!wall[x][y][3])   dfs(x+1,y);
}
int main()
{
    ifstream fin("castle.in");
    ofstream fout("castle.out");

    int M,N;
    while(fin>>M>>N)
    {
        memset(wall,0,sizeof(wall));
        memset(f,0,sizeof(f));
        memset(room,0,sizeof(room));
        memset(roomSize,0,sizeof(roomSize));

        for(int i = 1;i <= N;i ++){
            for(int j = 1;j <= M;j ++){
                int temp;
                fin>>temp;
                for(int k = 0;k < 4;k ++)   wall[i][j][k] = (temp>>k) & 1;     //利用位运算得到墙壁状态
            }
        }
        for(int i = 1;i <= N;i ++){
            for(int j = 1;j <= M;j ++){
                if(!f[i][j]){
                    roomN++;        //房间数目,同时也是房间编号
                    dfs(i,j);
                    Msize = (roomSize[roomN] > Msize) ? roomSize[roomN] : Msize;
                                                //一次dfs结束就找到了一个房间
                                                //在图中相当于找得到了一个连通子图。因此更新Msize
                }
            }
        }
        fout<<roomN<<endl<<Msize<<endl;
    }
/*****************************************************************************************************************
            根据题意是  N 和 E,这两个方向优先考虑。因此遍历方向( 左下 -> 右上,先列后行)
            roomA : 当前房间编号
            roomB : 当前房间上方空间的编号  (即北方 N)
            roomC : 当前房间右方空间的编号  (即东方 E)
            ans_x,ans_y : 空间坐标
            di : 目标墙壁推动的方向
*****************************************************************************************************************/
    Msize=0;
    int roomA,roomB,roomC;
    int ans_x,ans_y;
    char di;

    for(int i = 1;i <= M;i ++){
        for(int j = N;j >= 1;j --){
            roomA=room[j][i];
            roomB=room[j-1][i];
            roomC=room[j][i+1];

        //概括讲就是。存在墙壁 && 相邻 && 不属于同一间房子 && 房间大小 > 当前最大大小. 然后更新相关数据即可
            if(j > 1 && wall[j][i][1] && roomA != roomB && roomSize[roomA]+roomSize[roomB] > Msize){
                Msize=roomSize[roomA]+roomSize[roomB];
                ans_x=j;
                ans_y=i;
                di='N';
            }
            else if(i < M && wall[j][i][2] && roomA != roomC && roomSize[roomA]+roomSize[roomC] > Msize){
                Msize=roomSize[roomA]+roomSize[roomC];
                ans_x=j;
                ans_y=i;
                di='E';
            }
        }
    }
    fout<<Msize<<endl<<ans_x<<" "<<ans_y<<" "<<di<<endl;

    fin.close();
    fout.close();
    return 0;
}


转载于:https://www.cnblogs.com/Jstyle-continue/p/6352026.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值