Hard
题目描述
给定一个 n x n 的整数矩阵 grid,其中每个值 grid[i][j] 表示该点 (i, j) 的高度。
开始下雨,随着时间推移,水位逐渐上升。在时间 t,水位为 t,这意味着任何高度小于等于 t 的格子都被淹没或可达。
当且仅当两个相邻格子的高度都不超过 t 时,你才能从一个格子游泳到另一个四方向相邻的格子。你可以在零时间内游任意距离。当然,在游泳过程中你必须待在网格的边界内。
如果你从左上角的格子 (0, 0) 开始,返回能够到达右下角的格子 (n-1, n-1) 的最少时间。
示例 1:
输入: grid = [[0,2],[1,3]]
输出: 3
解释:
时间为0时,你在格子 (0, 0)。
你不能游到其他地方,因为四方向相邻的邻居都有更高的高度,超过了 t = 0。
直到时间为3,你才能到达格子 (1, 1)。
当水的深度为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²
- grid[i][j] 中的每个值都是唯一的
解题思路
这道题可以用多种方法求解,核心思想是找到最小的时间 t,使得从 (0,0) 到 (n-1,n-1) 存在一条路径,路径上所有格子的高度都不超过 t。
方法一:二分搜索 + BFS/DFS(推荐)
由于题目要求最小时间,且时间具有单调性(时间越大,能到达的区域越多),可以使用二分搜索。对于每个候选时间 t,使用 BFS 或 DFS 检查是否能从起点到达终点。
方法二:Dijkstra 算法
将问题转化为最短路径问题。每个格子作为图中的节点,相邻格子之间的边权重为两个格子高度的最大值。使用优先队列实现 Dijkstra 算法,找到从起点到终点的最小路径权重。
方法三:并查集
按高度从小到大遍历所有格子,逐步加入可通行区域。当起点和终点连通时,当前高度就是答案。
下面给出 Dijkstra 算法的实现,它在实际应用中通常表现最好。
代码实现
class Solution {
public:
int swimInWater(vector<vector<int>>& grid) {
int n = grid.size();
vector<vector<int>> dist(n, vector<int>(n, INT_MAX));
priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;
dist[0][0] = grid[0][0];
pq.push({grid[0][0], 0, 0});
vector<vector<int>> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
while (!pq.empty()) {
auto curr = pq.top();
pq.pop();
int time = curr[0], x = curr[1], y = curr[2];
if (x == n - 1 && y == n - 1) {
return time;
}
if (time > dist[x][y]) continue;
for (auto& dir : directions) {
int nx = x + dir[0], ny = y + dir[1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n) {
int newTime = max(time, grid[nx][ny]);
if (newTime < dist[nx][ny]) {
dist[nx][ny] = newTime;
pq.push({newTime, nx, ny});
}
}
}
}
return -1;
}
};
class Solution:
def swimInWater(self, grid: List[List[int]]) -> int:
import heapq
n = len(grid)
dist = [[float('inf')] * n for _ in range(n)]
pq = [(grid[0][0], 0, 0)]
dist[0][0] = grid[0][0]
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
while pq:
time, x, y = heapq.heappop(pq)
if x == n - 1 and y == n - 1:
return time
if time > dist[x][y]:
continue
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < n:
new_time = max(time, grid[nx][ny])
if new_time < dist[nx][ny]:
dist[nx][ny] = new_time
heapq.heappush(pq, (new_time, nx, ny))
return -1
public class Solution {
public int SwimInWater(int[][] grid) {
int n = grid.Length;
int[,] dist = new int[n, n];
var pq = new PriorityQueue<(int time, int x, int y), int>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dist[i, j] = int.MaxValue;
}
}
dist[0, 0] = grid[0][0];
pq.Enqueue((grid[0][0], 0, 0), grid[0][0]);
int[,] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
while (pq.Count > 0) {
var (time, x, y) = pq.Dequeue();
if (x == n - 1 && y == n - 1) {
return time;
}
if (time > dist[x, y]) continue;
for (int i = 0; i < 4; i++) {
int nx = x + directions[i, 0];
int ny = y + directions[i, 1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n) {
int newTime = Math.Max(time, grid[nx][ny]);
if (newTime < dist[nx, ny]) {
dist[nx, ny] = newTime;
pq.Enqueue((newTime, nx, ny), newTime);
}
}
}
}
return -1;
}
}
var swimInWater = function(grid) {
const n = grid.length;
const directions = [[0,1], [1,0], [0,-1], [-1,0]];
let left = Math.max(grid[0][0], grid[n-1][n-1]);
let right = n * n - 1;
const canReach = (time) => {
if (grid[0][0] > time || grid[n-1][n-1] > time) return false;
const visited = Array(n).fill().map(() => Array(n).fill(false));
const queue = [[0, 0]];
visited[0][0] = true;
while (queue.length > 0) {
const [x, y] = queue.shift();
if (x === n-1 && y === n-1) return true;
for (const [dx, dy] of directions) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < n &&
!visited[nx][ny] && grid[nx][ny] <= time) {
visited[nx][ny] = true;
queue.push([nx, ny]);
}
}
}
return false;
};
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (canReach(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
};
复杂度分析
| 算法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| Dijkstra | O(n²log(n²)) | O(n²) |
| 二分搜索 + BFS | O(n²log(n²)) | O(n²) |
| 并查集 | O(n²α(n²)) | O(n²) |
其中 n 是网格的边长,α 是阿克曼函数的反函数。
相关题目
- . Path With Minimum Effort (Medium)