这个题解的思路分为两个步骤:
1. 使用并查集判断初始时有多少个连通分量。如果连通分量数为0或大于2,那么答案为0,不需要改变任何陆地。
2. 如果只有一个连通分量,则使用Tarjan算法求割点。如果存在割点,则只需要删除一个割点就可以将图分成两部分,答案为1;否则需要删除两个点,答案为2。特殊情况是当全部都是陆地时,需要返回min(2, m*n)。
时间复杂度: O(m*n)
空间复杂度: O(m*n)
class UnionFind:
def __init__(self, n):
self.p = [i for i in range(n)]
self.k = n
def find(self, node):
if node != self.p[node]:
self.p[node] = self.find(self.p[node]) # 路径压缩
return self.p[node]
def union(self, a, b):
root_a, root_b = self.find(a), self.find(b)
if root_a != root_b:
self.p[root_a] = root_b # 合并连通分量
self.k -= 1 # 连通分量数减1
def tarjan_p(g, root, n):
count = 1
dfn, low, cut = [0] * n, [n] * n, [False] * n
def tarjan_p_inner(x, fa):
nonlocal count
dfn[x] = low[x] = count
count += 1
cnt = 0
for y in g[x]:
if y == fa:
continue
if dfn[y]:
low[x] = min(low[x], dfn[y])
else:
tarjan_p_inner(y, x)
low[x] = min(low[x], low[y])
if low[y] >= dfn[x]:
cnt += 1
if cnt > 1 or (x != root and cnt): # 判断割点
cut[x] = True
tarjan_p_inner(root, -1)
return [i for i, c in enumerate(cut) if c]
class Solution:
def minDays(self, grid: List[List[int]]) -> int:
# 目标是增大联通分量,能最小分成两部分就行
# 第一步:并查集,如果有0或大于2个连通域,答案就是0
# 第二步,tarjan判断割点,如果有答案是1,否则就是2
root = zero = 0
m, n = len(grid), len(grid[0])
uf = UnionFind(m * n)
g = [[] for _ in range(m*n)]
for i in range(m):
for j in range(n):
x = i * n + j
if i+1 < m and grid[i][j] and grid[i+1][j]:
uf.union(x, x+n)
g[x].append(x+n)
g[x+n].append(x)
root = x
if j+1 < n and grid[i][j] and grid[i][j+1]:
uf.union(x, x+1)
g[x].append(x+1)
g[x+1].append(x)
if not grid[i][j]:
zero += 1 # 统计水的数量
if uf.k - zero != 1: # 如果连通分量不为1,答案为0
return 0
ans = tarjan_p(g, root, m*n)
return 1 if ans else min(2, m*n-zero) # 根据割点判断最终答案