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;
}

猜你喜欢

转载自blog.csdn.net/qq_43738331/article/details/112787788