目录
一、题目描述
在一个 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] 的排列。
二、解题思路
这道题跟前一天的题目(leetCode1631)是换汤不换药。解法本质上是一样的。
示例里面已经说得很清楚了,这是一道图的连通性判断问题。图的连通性判定自然想到并查集、dfs、bfs等方法。仔细看提示的第二条会发现这是一个全为正整数且水位是有序增加的,没错,可以使用二分查找。
解法一:
利用二分找出最小的水位高度,同时对每个水位使用dfs或者bfs遍历判断是否存在起点到终点的路径即可。
解法二:
使用并查集,从雨水量为0开始进行合并,合并满足要求的位置(从四个方向探测符合要求的位置) ,边合并边判断是否收尾连通,若连通直接返回水量值。
三、代码实现
由于本人1631题使用的是二分+dfs,本题使用的是并查集。正好这两题本质 是一样的,所以就把两题的代码都贴出来。
/*!
*
* 力扣778:水位上升的泳池中游泳
*
*/
#include<bits/stdc++.h>
using namespace std;
vector<int> father;
void init(int n) {
for (int i = 0; i < n; i++) {
father.push_back(i);
}
}
int findFather(int a) {
if (a != father[a]) {
father[a] = findFather(father[a]);
}
return father[a];
}
void Union(int a, int b) {
int fa = findFather(a);
int fb = findFather(b);
if (fa != fb) {
father[fa] = fb;
}
}
bool isConnected(int a, int b) {
return findFather(a) == findFather(b);
}
vector<int> dx = { 1,-1,0,0 };
vector<int> dy = { 0,0,1,-1 };
int swimInWater(vector<vector<int>>& grid) {
int n = grid.size();
init(n*n);
//按照高度从小到大依次遍历,返回第一次满足条件的高度
//并查集的起点是高度为0的那个位置,所以还需要知道各个高度在图中的位置
//得用一个数组映射高度和位置关系
vector<int> height(n*n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
height[grid[i][j]] = i * n + j;
}
}
for (int i = 0; i < n*n; i++) {
int x = height[i] / n;
int y = height[i] % n;
//朝四个方向探测,合并小于或等于高度的位置
for (int k = 0; k < 4; k++) {
int new_x = x + dx[k];
int new_y = y + dy[k];
if (new_x >= 0 && new_x < n && new_y >= 0 && new_y < n && grid[new_x][new_y] <= i) {
Union(x * n + y, new_x * n + new_y);
}
//判断头尾是否连通
if (isConnected(0, n * n - 1)) {
return i;
}
}
}
return -1;
}
int main() {
vector<vector<int>> 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} };
cout << swimInWater(grid);
return 0;
}
/*!
*
* 力扣1631:最小体力消耗路径
* 并查集/二分+dfs
*/
#include<bits/stdc++.h>
using namespace std;
int res = INT_MAX;
vector<int> dx = { 1, -1, 0, 0 };
vector<int> dy = { 0, 0, 1, -1 };
int m = 0, n = 0;
void dfs(int a, int b, vector<vector<int>>& heights, int cost, vector<vector<bool>>& vis) {
if (a == m - 1 && b == n - 1) {
res = min(res, cost);
}
vis[a][b] = false;
for (int i = 0; i < 4; i++) {
int x = a + dx[i];
int y = b + dy[i];
if (x >= 0 && x < m && y >= 0 && y < n && vis[x][y] && abs(heights[x][y] - heights[a][b]) <= cost) {
dfs(x, y, heights, cost, vis);
}
}
}
int minimumEffortPath(vector<vector<int>>& heights) {
m = heights.size();
n = heights[0].size();
int l = 0, r = 1000000;
while (l < r) {
int mid = l + (r - l) / 2;
vector<vector<bool>> vis(m, vector<bool>(n, true));
dfs(0, 0, heights, mid, vis);
if (!vis[m - 1][n - 1]) {
r = mid;
} else {
l = mid + 1;
}
}
return res;
}
int main() {
vector<vector<int>> heights = { {1, 2, 3}, {3, 8, 4}, {5, 3, 5} };
cout << minimumEffortPath(heights);
return 0;
}