# 778. Swim in Rising Water

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/swim-in-rising-water/

> 内容描述

```
On an N x N grid, each square grid[i][j] represents the elevation at that point (i,j).

Now rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distance in zero time. Of course, you must stay within the boundaries of the grid during your swim.

You start at the top left square (0, 0). What is the least time until you can reach the bottom right square (N-1, N-1)?

Example 1:

Input: [[0,2],[1,3]]
Output: 3
Explanation:
At time 0, you are in grid location (0, 0).
You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0.

You cannot reach point (1, 1) until time 3.
When the depth of water is 3, we can swim anywhere inside the grid.
Example 2:

Input: [[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]]
Output: 16
Explanation:
 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

The final route is marked in bold.
We need to wait until time 16 so that (0, 0) and (4, 4) are connected.
Note:

2 <= N <= 50.
grid[i][j] is a permutation of [0, ..., N*N - 1].
```

## 解题方案

> 思路 1
******- 时间复杂度: O(N^2lgN)******- 空间复杂度: O(N^2)******

首先要看到一句话：```grid[i][j] is a permutation of [0, ..., N*N - 1]```， 这句话很重要

priority queue, beats 70.37%

```python
class Solution:
    def swimInWater(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        heap, seen, res = [(grid[0][0], 0, 0)], set([(0, 0)]), 0
        while True:
            T, i, j = heapq.heappop(heap)
            res = max(res, T)
            if i == j == len(grid) - 1:
                return res
            for x, y in [(i + 1, j), (i, j + 1), (i - 1, j), (i, j - 1)]:
                if 0 <= x < len(grid) and 0 <= y < len(grid) and (x, y) not in seen:
                    seen.add((x, y))
                    heapq.heappush(heap, (grid[x][y], x, y))
```


> 思路 2
******- 时间复杂度: O(N^3)******- 空间复杂度: O(N^2)******



union-find, 每隔一秒，我们就可以到达当前最大高度的那个点（即高度为time的那个点），然后从这个点继续往外走，如果旁边的点的高度小于等于time的话就可以走

每走一步time就加一秒，什么时候0和N^2-1相连了，就说明从(0,0)可以走到(N-1, N-1)了，此时返回time, beats 36.11%

```python
class UnionFind(object):
    def __init__(self, n):  # 初始化uf数组和组数目
        self.count = n
        self.uf = [i for i in range(n)]
        self.size = [1] * n # 每个联通分量的size

    def find(self, x):  # 判断节点所属于的组
        while x != self.uf[x]:
            self.uf[x] = self.uf[self.uf[x]]
            x = self.uf[x]
        return self.uf[x]

    def union(self, x, y):  # 连接两个节点
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root == y_root:
            return
        if self.size[x_root] < self.size[y_root]:
            x_root, y_root = y_root, x_root
        self.uf[y_root] = x_root
        self.size[x_root] += self.size[y_root]
        self.size[y_root] = 0
        self.count -= 1

    def connected(self, x, y):  # 判断两个节点是否联通
        return self.find(x) == self.find(y)

class Solution:
    def swimInWater(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        N = len(grid)
        location = {grid[i][j]: (i, j) for i in range(N) for j in range(N)}
        uf = UnionFind(N**2)
        
        for time in range(N**2):
            i, j = location[time]
            for x, y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                new_i, new_j = i + x, j + y
                if 0 <= new_i < N and 0 <= new_j < N and grid[new_i][new_j] <= time:
                    uf.union(i * N + j, new_i * N + new_j)
                    if uf.connected(0, N**2-1):
                        return time
```



> 思路 3
******- 时间复杂度: O(N^2lgN)******- 空间复杂度: O(N^2)******

二分 + DFS

最多时间为N^2-1, 最少为0，我先看看mid能不能到，不能就继续二分，每一次看能不能到都是一个新的dfs（即全新的空的visited）

beats 27.78%

```python
class Solution:
    def swimInWater(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        self.N = len(grid)
        # self.visited = {}
        left, right = grid[0][0], self.N * self.N - 1
        while left < right:
            mid = left + (right - left) // 2
            if self.canReach(grid, mid, 0, 0, {}):
                right = mid
            else:
                left = mid + 1
        return left
    
    def canReach(self, grid, time, i, j, visited):
        visited[(i,j)] = 1
        for x, y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            new_i, new_j = i + x, j + y
            if 0 <= new_i < self.N and 0 <= new_j < self.N and \
                    (new_i, new_j) not in visited and grid[new_i][new_j] <= time:
                if new_i == new_j == self.N - 1:
                    return True
                if self.canReach(grid, time, new_i, new_j, visited):
                    return True
        return False
```


















































