📜  门|门CS 2011 |第 43 题(1)

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

题目介绍:门|门CS 2011 |第 43 题

题目描述

给出一个 $n\times m$ 的矩阵,每一个位置用 $0$ 或 $1$ 标记。定义一个连通块是由相邻的格子组成的一片区域,包含了所有的 $1$,并且这片区域不能扩大,也不能缩小。

现在对于每个连通块,我们考虑它在整个矩阵中形如一个门,具体地,我们认为一个连通块是一个门,当且仅当:

  • 连通块里的所有 $1$ 都在一个外围,也就是说,可以通过从外部遍历连通块中的所有 $1$,来到达连通块中的任意一个 $1$,且不离开连通块。
  • 门可以旋转 $0$,$90$,$180$ 或 $270$ 度而不影响它是否是一个门。

求有多少个不同的门。可以保证每个连通块包含至少一个 $1$。

解法

我们可以先通过 DFS 或 BFS 找到每个连通块,然后对于每个连通块,可以考虑根据其中所有的 $1$ 的坐标,找到最远的 $1$,并将其当作门的上端点。

考虑将门顺时针旋转 $90$ 度的效果,失去支撑的点就会掉下去。那么显然我们只需要考虑最远的 $1$ 的上、下、左、右四个方向中,有哪些方向上的所有 $1$ 能够通过一条连续的路径连上另外一边。如果这样的连通块不止一个,显然它们构成的门是相同的。

时间复杂度为 $\mathcal{O}(nm\log nm)$。

代码

以下是一个使用 DFS 实现的解答:

from typing import List

class Solution:
    def findGates(self, matrix: List[List[int]]) -> int:
        def dfs(i, j):
            nonlocal x, y, visited
            visited[i][j] = True
            for dx, dy in ((0, 1), (1, 0), (0, -1), (-1, 0)):
                ni, nj = i + dx, j + dy
                if 0 <= ni < n and 0 <= nj < m and not visited[ni][nj] and matrix[ni][nj]:
                    if nj < y:
                        y = nj
                    dfs(ni, nj)
                    if j < y:
                        x, y = i, j
                    elif j == y:
                        x = min(x, i)

        n, m = len(matrix), len(matrix[0])
        visited = [[False] * m for _ in range(n)]
        ans = set()
        for i in range(n):
            for j in range(m):
                if not visited[i][j] and matrix[i][j]:
                    x, y = n - 1, m - 1
                    dfs(i, j)
                    top, bottom, left, right = x, x, y, y
                    que = [(x, y)]
                    visited[x][y] = True
                    while que:
                        i, j = que.pop()
                        top, bottom, left, right = min(top, i), max(bottom, i), min(left, j), max(right, j)
                        for dx, dy in ((0, 1), (1, 0), (0, -1), (-1, 0)):
                            ni, nj = i + dx, j + dy
                            if 0 <= ni < n and 0 <= nj < m and not visited[ni][nj] and matrix[ni][nj]:
                                if ni < x or (ni == x and nj <= y):
                                    y = nj
                                visited[ni][nj] = True
                                que.append((ni, nj))
                    ans.add((top - x + 1, right - left + 1))  # 标准化形状,取门区域最小的那个位置作为左上角,进行标准化
        return len(ans)