📅  最后修改于: 2023-12-03 15:28:48.940000             🧑  作者: Mango
给出一个 $n\times m$ 的矩阵,每一个位置用 $0$ 或 $1$ 标记。定义一个连通块是由相邻的格子组成的一片区域,包含了所有的 $1$,并且这片区域不能扩大,也不能缩小。
现在对于每个连通块,我们考虑它在整个矩阵中形如一个门,具体地,我们认为一个连通块是一个门,当且仅当:
求有多少个不同的门。可以保证每个连通块包含至少一个 $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)