并查集 | 二分查找 + BFSLeetCode 778. 水位上升的泳池中游泳
Posted 书所集录
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集 | 二分查找 + BFSLeetCode 778. 水位上升的泳池中游泳相关的知识,希望对你有一定的参考价值。
2021 第 14 篇题解
778. 水位上升的泳池中游泳
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/swim-in-rising-water/
题目
在一个 N x N 的坐标方格 grid
中,每一个方格的值 grid[i][j]
表示在位置 (i,j)
的平台高度。
现在开始下雨了。当时间为 t
时,此时雨水导致水池中任意位置的水位为 t
。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)
?
示例 1:
输入: [[0,2],[1,3]]
输出: 3
解释:
时间为0时,你位于坐标方格的位置为 (0, 0)。
此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为 0 时的水位。
等时间到达 3 时,你才可以游向平台 (1, 1). 因为此时的水位是 3,坐标方格中的平台没有比水位 3 更高的,所以你可以游向坐标方格中的任意位置
示例2:
输入: [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]]
输出: 16
解释:
0 1 2 3 4
24 23 22 21 5
12 13 14 15 16
11 17 18 19 20
10 9 8 7 6
最终的路线用加粗进行了标记。
我们必须等到时间为 16,此时才能保证平台 (0, 0) 和 (4, 4) 是连通的
提示:
-
2 <= N <= 50
. -
grid[i][j]
是[0, ..., N*N - 1]
的排列。
解题思路
思路:二分查找 + BFS,并查集
先审题,首先题目给定一个 的网格 (这里表示水池),其中每个方格中的值 表示的是水池位置 的平台高度。
现在题目给出的场景是这样的:⚡天下雨了☔,当时间为 时,雨水会导致水池中任意平台位置的水位都为 。题目允许从一个平台游向其他任意的平台,这里的前提是两个平台同时被雨水淹没。
其中在限定的网格中,假设可以瞬间移动无限距离,即是默认游动不耗时,问从网格左上方 出发,到达网格右下方 处最小耗时要多久?
首先先看下题意中需要注意的部分:
-
时间为 时,雨水会导致水池中任意平台位置的水位为 ; -
假设可以瞬间移动无限距离,在平台同时被雨水淹没时可以任意游动。
也就是说,题目要找到一个时刻 ,使得水池平台高度小于或等于 的部分,存在一条由左上角通往右下角的路径。
二分查找 + BFS
根据上面的题意可知,当 越大时,水池中平台可游动的部分越多;反之当 越小时,可游动的部分越小(因为平台高度较大的可能还未被淹没)。
题目最后的提示中说明 是 的排列。那么我们可以使用二分查找的方法在 中找得一个最短时间。具体方法如下:
-
定义 、 分别指向 和 , 先取 中间值 ; -
从左上角开始出发,向四个方向进行遍历访问(本题解用 BFS 方法,这里注意已经访问的进行标记): -
如果此时能够找到一条路径,从左上角到达右下角,那么 落在 处,可继续查找是否存在更小的时刻 ; -
如果未能找到一条路径时,那么要找的 一定比此时的 值大,落在 中,往这个区间继续搜索。 -
持续查找,缩小区间,最终即可求得答案时刻 。
具体的实现代码如下。
class Solution:
def swimInWater(self, grid: List[List[int]]) -> int:
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
n = len(grid)
def bfs(threshold):
queue = collections.deque()
queue.append((0, 0))
# 注意标记已访问部分
visited = [[False] * n for i in range(n)]
visited[0][0] = True
while queue:
x, y = queue.popleft()
# 向四个方向进行遍历访问
for dx, dy in directions:
nx = x + dx
ny = y + dy
# 限定边界
# 同时防止重复访问
if 0 <= nx < n and 0 <= ny < n and not visited[nx][ny] and grid[nx][ny] <= threshold:
queue.append((nx, ny))
visited[nx][ny] = True
return visited[-1][-1]
left = 0
right = n * n - 1
while left < right:
mid = left + (right - left) // 2
# 注意判断 grid[0][0] <= mid
# 如果大于 mid,那么说明所求答案一定是大于 mid 的
if grid[0][0] <= mid and bfs(mid):
right = mid
else:
left = mid + 1
return left
并查集
这道题,我们也可以用并查集来解决。
我们将网格中的每个方格看出是图的顶点。因为时间 决定水池平台的水位,当考虑两个相邻的顶点时,只有平台高度小于或等于 时,才能够将两个顶点进行连通。
这里我们从小到大考虑时刻 ,每经过一个时刻,如果高度为 的方格相邻方格的高度都不大于 ,那么两个方格能够连通,对四个方位相邻的格子判断完之后,判断左上角与右下角方格是否连通。若能连通,那么此时的 即是答案,否则继续下个时刻 。
思路描述可能比较模糊,可直接看代码进行理解。
class UnionFind:
def __init__(self, n):
self.parent = [i for i in range(n)]
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root != y_root:
self.parent[x_root] = y_root
def connected(self, x, y):
return self.find(x) == self.find(y)
class Solution:
def swimInWater(self, grid: List[List[int]]) -> int:
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
n = len(grid)
# 因为需要从小到大考虑时刻 t,从平台高度 t 出发判断是否能与相邻的方块连通
# 所以这里需要存储每个平台高度对应的位置
idx = [0] * (n * n)
for i in range(n):
for j in range(n):
idx[grid[i][j]] = (i, j)
print(idx)
uf = UnionFind(n * n)
for t in range(n * n):
# 对高度为 t 的平台进行判断是否能与相邻四个方位的平台连通
x, y = idx[t]
for dx, dy in directions:
nx = x + dx
ny = y + dy
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] <= t:
# 尝试合并相邻的平台
uf.union(x * n + y, nx * n + ny)
# 检查左下角与右下角是否连通,
# 若能连通,此时的 t 即是答案
if uf.connected(0, n * n - 1):
return t
return -1
欢迎关注
如有错误,烦请指出,欢迎指点交流。若觉得写得还不错,麻烦点个赞 以上是关于并查集 | 二分查找 + BFSLeetCode 778. 水位上升的泳池中游泳的主要内容,如果未能解决你的问题,请参考以下文章 力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集) UVA - 10004 Bicoloring(判断二分图——交叉染色法 / 带权并查集) hdu 5652 India and China Origins 并查集+二分