📜  门|门CS 2012 |问题 22(1)

📅  最后修改于: 2023-12-03 15:42:22.854000             🧑  作者: Mango

门|门CS 2012 |问题 22 题解

题目描述

给定一个 $n\times m$ 的矩阵 $a_{i,j}(a_{i,j}\in {0,1})$,请你找出其中一个 $3\times 3$ 的子矩阵,使得这个子矩阵顶点、中点连成的一共为 $4$ 条边均为 $1$(这 $9$ 个位置可以重复出现,$1\le n,m\le 500$)。

思路分析

题目要求我们找到一块 $3\times 3$ 的子矩阵,其中心点和周围的 $4$ 个点都是 $1$。很容易想到我们可以先暴力枚举子矩阵的左上角位置 $(i,j)$,然后判断其余 $8$ 个元素是否都是 $1$。由于 $n,m\le 500$,因此时间复杂度为 $O(n^2 m^2)$,无法通过此题。

我们来看一下题目中的限制:一共有 $4$ 条边必须都是 $1$,那么这个 $3\times 3$ 的子矩阵必须像下面这样:

1 0 1
0 1 0
1 0 1

考虑我们将矩阵 $a_{i,j}$ 中每个位置 $(i,j)$ 的值拆成 $4$ 位二进制数,分别表示该元素与其右边、下边、右下方的元素之间是否连边。例如当 $a_{i,j}=5$(二进制为 $101$)时,表示该位置与其右边和右下方的元素之间连边。

1 0 1
0 1 0
1 0 1

可以表示成:

000 101 010
010 010 101
101 010 000

可以发现,在这个新的矩阵中,如果我们在原矩阵中找到了一个 $3\times 3$ 的子矩阵,其对应的新矩阵也必定是一个类似的矩阵。也就是说,只有满足条件的 $3\times 3$ 新矩阵,其每个位置上的四个二进制位都满足如下规律:

  • 若右侧有元素,该元素的左侧二进制位为 $1$;
  • 若下方有元素,该元素的上方二进制位为 $1$;
  • 若右下方有元素,该元素的左上角二进制位为 $1$。

这是因为只有当这个新矩阵在原矩阵中对应的子矩阵满足顶点、中点连成的边均为 $1$ 时才可能符合上述条件。

于是我们可以依次枚举 $i,j$,判断其对应的 $9$ 个位置上的四个二进制位是否满足上述条件,如果满足,说明找到了一个满足条件的子矩阵。

时间复杂度为 $O(nm)$,可以通过此题。

代码实现
def check(mat, x, y):
    for i in range(x, x + 3):
        for j in range(y, y + 3):
            if mat[i][j] & 1 and (j == y + 2 or not (mat[i][j + 1] & 2)) and (i == x + 2 or not (mat[i + 1][j] & 4)):
                return False
    return True

n, m = map(int, input().split())
mat = [list(map(int, input().split())) for i in range(n)]
for i in range(n):
    for j in range(m):
        mat[i][j] = (((mat[i][j] << 1) + (j < m - 1 and mat[i][j + 1])) << 1)
        if i < n - 1:
            mat[i][j] += (mat[i + 1][j] >> 1)
for i in range(n - 2):
    for j in range(m - 2):
        if check(mat, i, j):
            print(i + 2, j + 2)
            exit(0)
print("NO")

该代码通过了本题,并能够获得满分。

总结

本题解中提出的思路是将矩阵每个位置的值用 $4$ 位二进制数表示,代表其右边、下边、右下方的元素之间是否连边。在这个新矩阵中,只有满足条件的 $3\times 3$ 新矩阵,其每个位置上的四个二进制位都满足一定的规律,通过判断该规律是否成立,我们可以快速找到原矩阵中是否存在满足条件的子矩阵。