USACO城堡(并查集)

农夫约翰购买的彩票中了大奖,这让他赢得了一座坐落于爱尔兰乡村的如同神话一般的城堡!

约翰想要将关于城堡的一切统统告诉奶牛,让它们一起分享他的快乐。

在这之前,他需要知道城堡中共有多少个房间,最大的房间有多大。

此外,他还想拆掉一堵墙从而腾出一个更大的房间来。

城堡的平面图可以被划分为 N×M个方格区域,每个方格区域可以有0~4面墙。

当然,城堡的外边缘一定都是墙壁,从而防止风吹雨打。

参考一下下面这个带注释的城堡平面图:

     1   2   3   4   5   6   7
   #############################
 1 #   |   #   |   #   |   |   #
   #####---#####---#---#####---#   
 2 #   #   |   #   #   #   #   #
   #---#####---#####---#####---#
 3 #   |   |   #   #   #   #   #   
   #---#########---#####---#---#
 4 # ->#   |   |   |   |   #   #   
   ############################# 

#  = 墙壁     -,|  = 没有墙壁
-> = 将箭头指向的这面墙拆除就可以得到最大的房间
注意:地图方向为:上北下南左西右东。

如图所示,这个城堡是由 4×7 个方格区域构成,相连的方格区域构成一个大的房间,该平面图中共包含 5 个房间(大小分别为 9、7、3、1、8)。

将箭头所指的那面墙移除之后,两个房间之间被打通,共同组成了一个新的最大的房间,

城堡保证至少拥有两个房间,一定拥有可以被拆掉的墙。

请你帮忙计算一下,城堡中共有多少个房间,最大的房间的面积是多少,通过移除一面墙可以获得的房间的最大面积是多少,移除哪面墙可以获得面积最大的房间。

输入格式
第一行包含两个整数 M,N,分别表示城堡东西方向的长度和南北方向的长度。

接下来 N 行,每行包含 M 个整数,每个整数都表示平面图对应位置的方块的墙的特征。

每个方块中墙的特征由数字 P 来描述,我们用1表示西墙,2表示北墙,4表示东墙,8表示南墙,P 为该方块包含墙的数字之和。

例如,如果一个方块的 PP 为3,则 3 = 1 + 2,该方块包含西墙和北墙。

城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。

输出格式
输出共包含四行。

第一行输出房间总数。

第二行输出最大房间面积。

第三行输出移除一面墙可以获得的房间的最大面积。

第四行输出移除哪面墙可以获得面积最大的房间,形容该面墙时,输出墙体西侧或南侧方格区域的行和列以及该墙在方格区域中的位置方位(东用E表示,北用N表示)。

例如,我们要拆掉图例中箭头所指的墙,该墙南北是墙体,东西是方格区域,因此用它西侧的方格区域形容它的位置,它位于(4,1)方格的东侧,因此它的位置表示为 4 1 E。

当移除方法不唯一时,优先选择对应方格区域更靠西的墙,如果仍存在多解,选择对应方格区域更靠南的墙。

在此基础上,还存在多解(即可选的两个墙都对应同一方格区域),那么优先选择北面的墙。

数据范围
1≤M,N≤50
输入样例:
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
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 55, M = N * N;

int n, m;
int g[N][N];
int p[M], sz[M];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> m >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
            cin >> g[i][j];

    for (int i = 0; i < n * m; i ++ )//并查集初始化
    {
        p[i] = i;
        sz[i] = 1;
    }

    int dx[2] = {-1, 0}, dy[2] = {0, 1}, dw[2] = {2, 4};
    int cnt = n * m, max_area = 1;//房间最小是1
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
            for (int u = 0; u < 2; u ++ )
                if (!(g[i][j] & dw[u]))//可以往这个方向走
                {
                    int x = i + dx[u], y = j + dy[u];
                    if (x < 0 || x >= n || y < 0 || y >= m) continue;
                    int a = i * m + j, b = x * m + y;
                    a = find(a), b = find(b);
                    if (a != b)
                    {
                        cnt -- ;
                        sz[b] += sz[a];
                        p[a] = b;
                        max_area = max(max_area, sz[b]);
                    }
                }

    cout << cnt << endl << max_area << endl;
    max_area = 0;
    int rx, ry, rw;

    for (int j = 0; j < m; j ++ )
        for (int i = n - 1; i >= 0; i -- )
            for (int u = 0; u < 2; u ++ )
                if (g[i][j] & dw[u])//这个方向有墙壁
                {
                    int x = i + dx[u], y = j + dy[u];
                    if (x < 0 || x >= n || y < 0 || y >= m) continue;
                    int a = i * m + j, b = x * m + y;
                    a = find(a), b = find(b);
                    if (a != b)
                    {
                        int area = sz[a] + sz[b];
                        if (area > max_area)
                        {
                            max_area = area;
                            rx = i, ry = j, rw = u;
                        }
                    }
                }

    cout << max_area << endl;
    cout << rx + 1 << ' ' << ry + 1 << ' ' << (rw ? 'E': 'N') << endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王子y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值