图论刷题-4力扣 778. 水位上升的泳池中游泳
Posted smile-yan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论刷题-4力扣 778. 水位上升的泳池中游泳相关的知识,希望对你有一定的参考价值。
图论刷题
778. 水位上升的泳池中游泳
力扣原题 地址
难度与标签
困难难度
- 深度优先遍历
- 广度优先遍历
- 并查找
题目描述
在一个 n x n
的整数矩阵 grid
中,每一个方格的值 grid[i][j]
表示位置 (i, j)
的平台高度。
当开始下雨时,在时间为 t
时,水池中的水位为 t
。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 (0,0)
出发。返回 你到达坐标方格的右下平台 (n-1, n-1)
所需的最少时间 。
示例 1:
输入: grid = [[0,2],[1,3]]
输出: 3
解释:
时间为0时,你位于坐标方格的位置为 (0, 0)。
此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为 0 时的水位。
等时间到达 3 时,你才可以游向平台 (1, 1). 因为此时的水位是 3,坐标方格中的平台没有比水位 3 更高的,所以你可以游向坐标方格中的任意位置
示例 2:
输入: grid = [[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
解释: 最终的路线用加粗进行了标记。
我们必须等到时间为 16,此时才能保证平台 (0, 0) 和 (4, 4) 是连通的
提示:
n == grid.length
n == grid[i].length
1 <= n <= 50
0 <= grid[i][j] < n^2
grid[i][j] 中每个值 均无重复
题目分析
题目很有意思,但确实很难。首先简单的理解场景可以假设我们一只鸭子,要从 [0, 0]
游回家 [n-1, n-1]
。注意是 游
回家,所以水要超过高台才表示这条路是通的,不然水比台子低不能游过去。
现在结合图1,一定要等水位到了 3 才可以通行。
接着我们给出我们读题目的重要结论:
- 假设
W = Max(grid)
,即 W 是二维数组最大的那个数,则等水位到了W
一定有通路,可爱的小鸭子一定能回家。 - 我们的答案一定在
[0, W]
之间。
所以这个题目转换成寻找一个阈值 threshold
∈
[
0
,
W
]
\\in [0, W]
∈[0,W] ,使得,当水位到 threshold
时,可以得到至少一条从 [0, 0]
到 [n-1, n-1]
回家的路。
我们拥有一个二维数组 ary
,尺寸为 n x n
,默认全部为 0.
步骤如下:
- 假设阈值为
Q
,对二维数组ary
进行对应位置的赋值操作,具体规则为:如果 grid[i][j] <= 阈值 Q,则 ary[i][j] = 1,表示这个点可以过。
- 遍历 ary,查找是否存在通路
[0, 0]
到[n-1, n-1]
。
很明显,如果 Q
越大,则可以通行的点就越多。
代码实现 1—— DFS + 二分查询
- 初始化
visited
数组,visited[i][j]
为true
表示已经访问或者不能访问。 - 编写一个DFS函数,遍历所有路径。如果能够达到目的地(回家)就返回 true;
- 二分查找阈值,每次根据阈值初始化 visited 数组,然后调用 DFS 函数检查是否可以访问。
重要补充
:grid[0][0]
位置不一定为 0
,所以输出的结果 target
一定大于等于 grid[0][0]
,因此二分查找时起点为 grid[0][0]
,根据题目条件最大值也小于 n*n
,所以二分查找的终点是 n*n-1
。
class Solution
public:
const int dx[4] = 0, 1, 0, -1;
const int dy[4] = 1, 0, -1, 0;
/**
* 初始化 visited 数组,把不符合条件的点置于 true
*/
void initVisited(vector<vector<int>> grid,
vector<vector<bool>>& visited,
int threshold)
int n = grid.size();
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
visited[i][j] = grid[i][j] > threshold;
/**
* dfs 遍历
*/
bool dfs(vector<vector<bool>>& visited,
int x, int y)
int n = visited.size();
// 1. 判断是否到达终点
if (x == n-1 && y == n-1)
return true;
// 2. 标记已经访问
visited[x][y] = true;
// 3. 深度优先遍历
for (int i=0; i<4; i++)
int mx = x + dx[i];
int my = y + dy[i];
if (mx >= 0 && mx < n && my >= 0 && my < n && !visited[mx][my] )
// 如果到达终点才 return 否则 continue for循环
if (dfs(visited, mx, my))
return true;
return false;
int swimInWater(vector<vector<int>>& grid)
int n = grid.size();
int left = grid[0][0];
int right = n*n-1;
vector<vector<bool>> visited(n, vector<bool>(n));
while (left < right)
int mid = (left+right)/2;
initVisited(grid, visited, mid);
if (dfs(visited, 0, 0))
right = mid;
else
left = mid+1;
// left == right
return left;
;
- 时间复杂度: O ( N 2 log N ) O(N^2 \\log N) O(N2logN)
- 空间复杂度: O ( N 2 ) O(N^2) O(N2)
补充说明:如果题目中没有强调每个点的取值范围,那么可以首先找到最大的和最小的,再进行二分查询,一样的效果,不影响总体时间。
另外,这道题并不是最短路径问题,关注的是有多少水的时候小鸭子可以通行,因此,即便最后可能存在很多条路径,我们只需要确保有一条可以同行即可。
总结
跟官方的题解基本上是一样的,但是算法1中自己的想法是把额外条件附加在 visited
数组上, dfs 基本上没有什么变化。
如果理解了这道题目的意思,唯一容易出错的应该是 二分法查找阈值。事实上我是先通过所有测试用例并提示说超时,看了官方源码才想起用二分法查找的。
总而言之是一道有意思的题目,千万不能被 困难
两个字唬住了。
Smileyan
2022.3.22 17:08
以上是关于图论刷题-4力扣 778. 水位上升的泳池中游泳的主要内容,如果未能解决你的问题,请参考以下文章
并查集 | 二分查找 + BFSLeetCode 778. 水位上升的泳池中游泳