在一个 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] 的排列。
思路:
并查集问题(在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中),解决方法是二分法(看到有最大取值N*N - 1,想到用二分法)+BFS。
定义 left、right 分别指向0和N*N - 1, 先取中间值mid;(这个序列表示的是t的取值范围)
从左上角开始出发,向四个方向进行遍历访问(本题解用 BFS 方法+访问标记):
如果此时能够找到一条路径,从左上角到达右下角,那么t落在[left, mid]处,可继续查找是否存在更小的时刻 ;
如果未能找到一条路径时,那么要找的 一定比此时的t值大,落在[mid+1, right]中,往这个区间继续搜索。
结束条件是访问到了grid[N-1][N-1]且<t,那么返回true。
class Solution { public int swimInWater(int[][] grid) { int N = grid.length; int left = Math.max(grid[0][0], grid[N-1][N-1]); int right = N*N - 1; BitSet bs = new BitSet(right); int mid; while(left < right) { mid = (left + right) >> 1; if(dfs(grid, 0, 0, mid, bs)) right = mid; else left = mid+1; bs.clear(); } return left; } private boolean dfs(int[][]grid, int i, int j, int threshold, BitSet bs){ if(bs.get(i*grid.length+j) || grid[i][j] > threshold) return false; if(i == grid.length-1 && j == grid.length-1) return true; bs.set(i*grid.length + j); if(i > 0 && dfs(grid, i-1, j, threshold, bs)) return true; if(j > 0 && dfs(grid, i, j-1, threshold, bs)) return true; if(i < grid.length-1 && dfs(grid, i+1, j, threshold, bs)) return true; if(j < grid.length-1 && dfs(grid, i, j+1, threshold, bs)) return true; return false; } }