Weekly Contest 70 leetcode 778. Swim in Rising Water

257 篇文章 17 订阅

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:

  1. 2 <= N <= 50.
  2. grid[i][j] is a permutation of [0, ..., N*N - 1].
这道题我本来打算用dp做,但是发现不对。比如说在12这个位置,它可能从【上、右、下】游过来,并不能靠单纯单方向的dp得到。

我的思路有点像最小生成树的prim算法。使用一个优先级队列。先压入元素0,然后pop出队首,找元素0的相邻元素,再压入队列。每次从队列中pop出的都是当前能连通到的最小元素。一直到能连通到右下角目标元素就结束,在这个过程中pop出的元素(即走的路径元素)中最大的那个就是result。

package leetcode;

import java.util.PriorityQueue;

public class Swim_in_Rising_Water_778 {

	public int swimInWater(int[][] grid) {
		int n=grid.length;
		int[][] direction=new int[][]{
			{-1,0},{1,0},{0,-1},{0,1}//上下左右
		};
		boolean[][] visited=new boolean[n][n];
		PriorityQueue<Coordinate> pq=new PriorityQueue<Coordinate>((a,b) -> a.val-b.val);
		pq.offer(new Coordinate(0, 0, grid[0][0]));
		int max=0;
		while(!pq.isEmpty()){
			Coordinate c=pq.poll();
			visited[c.x][c.y]=true;
			max=Math.max(max, c.val);
			if(c.x==n-1&&c.y==n-1){				
				break;
			}
			for(int i=0;i<4;i++){
				int newX=c.x+direction[i][0];
				int newY=c.y+direction[i][1];
				if(newX<0||newX>=n||newY<0||newY>=n||visited[newX][newY]==true){
					continue;
				}
				pq.offer(new Coordinate(newX, newY, grid[newX][newY]));
			}
		}
		return max;
	}
	
	class Coordinate{
		int x;
		int y;
		int val;
		public Coordinate(int x,int y,int val){
			this.x=x;
			this.y=y;
			this.val=val;
		}
	}
}
这道题有solutions,其中方法1的思路跟我的一样。 https://leetcode.com/problems/swim-in-rising-water/solution/
Approach #1: Heap [Accepted]

Intuition and Algorithm

保持一个优先级队列,队列中保存我们下一步可以去的方格。 我们下一步总会去“是访问过的结点的相邻4个方向的结点中最小的那个”。
当我们达到目标右下角时,迄今为止访问最大的数字就是答案。

class Solution {
    public int swimInWater(int[][] grid) {
        int N = grid.length;
        Set<Integer> seen = new HashSet();
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>((k1, k2) ->
                grid[k1 / N][k1 % N] - grid[k2 / N][k2 % N]);
        pq.offer(0);
        int ans = 0;

        int[] dr = new int[]{1, -1, 0, 0};
        int[] dc = new int[]{0, 0, 1, -1};

        while (!pq.isEmpty()) {
            int k = pq.poll();
            int r = k / N, c = k % N;
            ans = Math.max(ans, grid[r][c]);
            if (r == N-1 && c == N-1) return ans;

            for (int i = 0; i < 4; ++i) {
                int cr = r + dr[i], cc = c + dc[i];
                int ck = cr * N + cc;
                if (0 <= cr && cr < N && 0 <= cc && cc < N && !seen.contains(ck)) {
                    pq.offer(ck);
                    seen.add(ck);
                }
            }
        }

        throw null;
    }
}

Complexity Analysis

  • Time Complexity: O(N^2 \log N)O(N2logN). We may expand O(N^2)O(N2) nodes, and each one requires O(\log N)O(logN) time to perform the heap operations.

  • Space Complexity: O(N^2)O(N2), the maximum size of the heap.


Approach #2: Binary Search and DFS [Accepted]

Intuition and Algorithm

“是否能游”是一个随时间变化的单调函数,我们可以对这个函数进行二分查找,寻找“能游”的最小 T 。
假设正确的时间是 T。为了检查是否能游,我们执行一个简单的深度优先搜索,检查在 T 时刻 是否能搜索到目标结点。

class Solution {
    public int swimInWater(int[][] grid) {
        int N = grid.length;
        int lo = grid[0][0], hi = N * N;  //根据题目最下行: grid[i][j]<=(N*N - 1)
        while (lo < hi) {
            int mi = lo + (hi - lo) / 2;
            if (!possible(mi, grid)) {
                lo = mi + 1;
            } else {
                hi = mi;
            }
        }
        return lo;
    }

    public boolean possible(int T, int[][] grid) {
        int N = grid.length;
        Set<Integer> seen = new HashSet();
        seen.add(0);
        int[] dr = new int[]{1, -1, 0, 0};
        int[] dc = new int[]{0, 0, 1, -1};

        Stack<Integer> stack = new Stack();
        stack.add(0);

        while (!stack.empty()) {
            int k = stack.pop();
            int r = k / N, c = k % N;
            if (r == N-1 && c == N-1) return true;

            for (int i = 0; i < 4; ++i) {
                int cr = r + dr[i], cc = c + dc[i];
                int ck = cr * N + cc;
                if (0 <= cr && cr < N && 0 <= cc && cc < N
                        && !seen.contains(ck) && grid[cr][cc] <= T) {
                    stack.add(ck);
                    seen.add(ck);
                }
            }
        }

        return false;
    }
}

Complexity Analysis

  • Time Complexity: O(N^2 \log N)O(N2logN). Our depth-first search during a call to possible is O(N^2)O(N2), and we make up to O(\log N)O(logN) of them.

  • Space Complexity: O(N^2)O(N2), the maximum size of the stack.

方法2中的possible函数跟方法1很像,只不过没有用优先级队列,把排序的O(logN) 时间转移到二分搜索的O(logN) 时间了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值