zoukankan      html  css  js  c++  java
  • [LeetCode] 1162. As Far from Land as Possible 地图分析


    Given an n x n grid containing only values 0 and 1, where 0 represents water and 1 represents land, find a water cell such that its distance to the nearest land cell is maximized, and return the distance. If no land or water exists in the grid, return -1.

    The distance used in this problem is the Manhattan distance: the distance between two cells (x0, y0) and (x1, y1) is |x0 - x1| + |y0 - y1|.

    Example 1:

    Input: grid = [[1,0,1],[0,0,0],[1,0,1]]
    Output: 2
    Explanation: The cell (1, 1) is as far as possible from all the land with distance 2.
    

    Example 2:

    Input: grid = [[1,0,0],[0,0,0],[0,0,0]]
    Output: 4
    Explanation: The cell (2, 2) is as far as possible from all the land with distance 4.
    

    Constraints:

    • n == grid.length
    • n == grid[i].length
    • 1 <= n <= 100
    • grid[i][j] is 0 or 1

    这道题给了一个只有0和1的二维数组,说是0表示水,1表示陆地,现在让找出一个0的位置,使得其离最近的1的距离最大,这里的距离用曼哈顿距离表示。这里最暴力的方法就是遍历每个0的位置,对于每个遍历到的0,再遍历每个1的位置,计算它们的距离,找到最小的距离保存为该0位置的距离,然后在所有0位置的距离中找出最大的。这种方法不是很高效,目测无法通过 OJ,博主都没有尝试。其实这道题的比较好的解法是建立距离场,即每个大于1的数字表示该位置到任意一个1位置的最短距离,在之前那道 Shortest Distance from All Buildings 就用过这种方法。建立距离场用 BFS 比较方便,因为其是一层一层往外扩散的遍历,这里需要注意的是要一次把所有1的位置都加入 queue 中一起遍历,而不是对每个1都进行 BFS,否则还是过不了 OJ。这里先把位置1都加入 queue,然后这里先做个剪枝,若位置1的个数为0,或者为 n^2,表示没有陆地,或者没有水,直接返回 -1。否则进行 while 循环,步数 step 加1,然后用 for 循环确保进行层序遍历,一定要将 q.size() 放到初始化中,因为其值可能改变。然后就是标准的 BFS 写法了,取队首元素,遍历其相邻四个结点,若没有越界且值为0,则将当前位置值更新为 step,然后将这个位置加入 queue 中继续遍历。循环退出后返回 step-1 即可,参见代码如下:


    解法一:

    class Solution {
    public:
        int maxDistance(vector<vector<int>>& grid) {
            int step = 0, n = grid.size();
            vector<vector<int>> dirs{{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
            queue<vector<int>> q;
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (grid[i][j] == 0) continue;
                    q.push(vector<int>{i, j});
                }
            }
            if (q.size() == 0 || q.size() == n * n) return -1;
            while (!q.empty()) {
                ++step;
                for (int k = q.size(); k > 0; --k) {
                    auto t = q.front(); q.pop();
                    for (auto dir : dirs) {
                        int x = t[0] + dir[0], y = t[1] + dir[1];
                        if (x < 0 || x >= n || y < 0 || y >= n || grid[x][y] != 0) continue;
                        grid[x][y] = step;
                        q.push({x, y});
                    }
                }
            }
            return step - 1;
        }
    };
    

    我们也可以强行用 DFS 来做,这里对于每一个值为1的点都调用递归函数,更新跟其相连的所有0位置的距离,最终也能算出整个距离场,从而查找出最大的距离,参见代码如下:


    解法二:

    class Solution {
    public:
        int maxDistance(vector<vector<int>>& grid) {
            int res = -1, n = grid.size();
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (grid[i][j] == 1) {
                        grid[i][j] = 0;
                        helper(grid, i, j);
                    }
                }
            }
            for (int i = n - 1; i >= 0; --i) {
                for (int j = n - 1; j >= 0; --j) {
                    if (grid[i][j] > 1) res = max(res, grid[i][j] - 1);
                }
            }
            return res;
        }
        void helper(vector<vector<int>>& grid, int i, int j, int dist = 1) {
            int n = grid.size();
            if (i < 0 || j < 0 || i >= n || j >= n || (grid[i][j] != 0 && grid[i][j] <= dist)) return;
            grid[i][j] = dist;
            helper(grid, i - 1, j, dist + 1);
            helper(grid, i + 1, j, dist + 1);
            helper(grid, i, j - 1, dist + 1);
            helper(grid, i, j + 1, dist + 1);
        }
    };
    

    其实这道题的最优解法并不是 BFS 或者 DFS,而是下面这种两次扫描的方法,在之前那道 01 Matrix 中就使用过。有点像动态规划的感觉,还是建立距离场,先从左上遍历到右下,遇到1的位置跳过,然后初始化0位置的值为 201(因为n不超过 100,所以距离不会超过 200),然后用左边和上边的值加1来更新当前位置的,注意避免越界。然后从右边再遍历到左上,还是遇到1的位置跳过,然后用右边和下边的值加1来更新当前位置的,注意避免越界,同时还要更新结果 res 的值。最终若 res 为 201,则返回 -1,否则返回 res-1,参见代码如下:


    解法三:

    class Solution {
    public:
        int maxDistance(vector<vector<int>>& grid) {
            int res = 0, n = grid.size();
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (grid[i][j] == 1) continue;
                    grid[i][j] = 201;
                    if (i > 0) grid[i][j] = min(grid[i][j], grid[i - 1][j] + 1);
                    if (j > 0) grid[i][j] = min(grid[i][j], grid[i][j - 1] + 1);
                }
            }
            for (int i = n - 1; i >= 0; --i) {
                for (int j = n - 1; j >= 0; --j) {
                    if (grid[i][j] == 1) continue;
                    if (i < n - 1) grid[i][j] = min(grid[i][j], grid[i + 1][j] + 1);
                    if (j < n - 1) grid[i][j] = min(grid[i][j], grid[i][j + 1] + 1);
                    res = max(res, grid[i][j]);
                }
            }
            return res == 201 ? -1 : res - 1;
        }
    };
    

    Github 同步地址:

    https://github.com/grandyang/leetcode/issues/1162


    类似题目:

    Shortest Distance from All Buildings

    01 Matrix


    参考资料:

    https://leetcode.com/problems/as-far-from-land-as-possible/

    https://leetcode.com/problems/as-far-from-land-as-possible/discuss/360963/C%2B%2B-with-picture-DFS-and-BFS

    https://leetcode.com/problems/as-far-from-land-as-possible/discuss/422691/7ms-DP-solution-with-example-beats-100


    LeetCode All in One 题目讲解汇总(持续更新中...)

  • 相关阅读:
    Golang 数组
    Golang 定时任务库使用
    Golang 选项设计模式
    使用阿里云构建海外docker镜像
    大数相加算法实现
    递归思想和迭代思想
    阶乘函数(factorial)——结果在整型范围内的阶乘计算
    最大公约数和最小公倍数(Greatest Common Divisor and Least Common Multiple)
    日结(2018年12月23日)
    日结(2018年12月22日)
  • 原文地址:https://www.cnblogs.com/grandyang/p/15037404.html
Copyright © 2011-2022 走看看