# 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].
import collections
import heapq


class Solution(object):
    def swimInWater_wrong(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        nei, N, level = [(0, 1), (1, 0), (-1, 0), (0, -1)], len(grid), 0
        start, end, visited = [(grid[0][0], 0, 0)], [(grid[-1][-1], N - 1, N - 1)], set()
        visited.add((grid[0][0], 0, 0))
        visited.add((grid[-1][-1], N - 1, N - 1))
        while start and end:
            if len(start) > len(end):
                start, end = end, start

            _next = []
            while start:
                l, x, y = heapq.heappop(start)
                level = max(level, grid[x][y])

                for dx, dy in nei:
                    if 0 <= x + dx < N and 0 <= y + dy < N and (grid[x + dx][y + dy], x + dx, y + dy) not in visited:
                        visited.add((grid[x + dx][y + dy], x + dx, y + dy))
                        if (grid[x + dx][y + dy], x + dx, y + dy) in end or (x + dx == N - 1 and y + dy == N - 1):
                            return level
                        heapq.heappush(_next, (grid[x + dx][y + dy], x + dx, y + dy))

            start = _next

        return level

    def swimInWater(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        nei, N, level = [(0, 1), (1, 0), (-1, 0), (0, -1)], len(grid), 0
        start, visited = [(grid[0][0], 0, 0)], set()
        visited.add((grid[0][0], 0, 0))

        while start:
            l, x, y = heapq.heappop(start)
            level = max(level, l)
            if x == N - 1 and y == N - 1:
                return level
            for dx, dy in nei:
                nx, ny = x + dx, y + dy
                if 0 <= nx < N and 0 <= ny < N and (grid[nx][ny], nx, ny) not in visited:
                    visited.add((grid[nx][ny], nx, ny))
                    heapq.heappush(start, (grid[nx][ny], nx, ny))

        return level



if __name__ == '__main__':
    print Solution().swimInWater(
        [[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]])
    print Solution().swimInWater([[0, 2], [1, 3]])
