1162、地图分析
你现在手里有一份大小为 N x N 的『地图』(网格) grid,上面的每个『区域』(单元格)都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地,你知道距离陆地区域最远的海洋区域是是哪一个吗?请返回该海洋区域到离它最近的所有的陆地区域的距离。
我们这里说的距离是『曼哈顿距离』( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个区域之间的距离是 |x0 - x1| + |y0 - y1| 。
如果我们的地图上只有陆地或者海洋,请返回 -1。
示例 1:

输入:[[1,0,1],[0,0,0],[1,0,1]]
输出:2
解释: 
海洋区域 (1, 1) 和所有陆地区域之间的距离都达到最大,最大距离为 2。
示例 2:

输入:[[1,0,0],[0,0,0],[0,0,0]]
输出:4
解释: 
海洋区域 (2, 2) 和所有陆地区域之间的距离都达到最大,最大距离为 4。
提示:
- 1 <= grid.length == grid[0].length <= 100
- grid[i][j]不是 0 就是 1
链接:https://leetcode-cn.com/problems/as-far-from-land-as-possible
题解
图论题目。
1、BFS-宽度有限搜索

class Solution {
    // 位移辅助变量
    int[] dx = new int[]{-1,0,1,0};
    int[] dy = new int[]{0,1,0,-1};
    int MAX_N = 100 + 5;
    class Coordinate {
        int x;
        int y;
        int step;
        public Coordinate(int x,int y,int step) {
            this.x = x;
            this.y = y;
            this.step = step;
        }
    }
    int n;
    // 浅拷贝
    int[][] a;
    boolean[][] vis;
    private int findNearestLand(int x,int y) {
        vis = new boolean[MAX_N][MAX_N];
        Queue<Coordinate> q = new LinkedList<Coordinate>();
        q.offer(new Coordinate(x,y,0));
        vis[x][y] = true;
        while (!q.isEmpty()) {
            Coordinate f = q.peek();
            q.poll();
            for (int i = 0;i < 4;i++) {
                int nx = f.x + dx[i],ny = f.y + dy[i];
                // 遇到地图边界,直接返回
                if (!(nx >= 0 && nx <= n - 1 && ny >= 0 && ny <= n -1)) {
                    continue;
                }
                // 当前位置没有被访问过
                if (!vis[nx][ny]) {
                    q.offer(new Coordinate(nx,ny,f.step + 1));
                    vis[nx][ny] = true;
                    // 是陆地,结束。找到了最近的陆地。
                    if (a[nx][ny] == 1){
                        return f.step + 1;
                    }
                }
            }
        }
        return -1;
    }
    public int maxDistance(int[][] grid) {
        this.n = grid.length;
        a = grid;
        int ans = -1;
        for (int i = 0;i < n;i++) {
            for (int j = 0;j < n;j++) {
                if(a[i][j] == 0) {
                    ans = Math.max(ans,findNearestLand(i,j));
                }
            }
        }
        return ans;
    }
}
2、多源最短路

- Dijkstra版本
class Solution {
    int MAX_N = 100 + 5;
    int INF = 1000000;
    // 位移辅助变量
    int[] dx = new int[]{-1,0,1,0};
    int[] dy = new int[]{0,1,0,-1};
    int n;
    int[][] d = new int[MAX_N][MAX_N];
    class Status implement Comparable<Integer> {
        int v,x,y;
        public Status(int v,int x,int y) {
            this.x = x;
            this.y = y;
            this.v = v;
        }
        @Override
        public int compareTo(Status s2) {
            return this.v - s2.v;
        }
    }
    Queue<Status> q =  new PriorityQueue<Status>();
    public int maxDistance(int[][] grid) {
        this.n = grid.size();
        
        // 初始化 d数组
        for (int i = 0;i < n;i++) {
            for (int j = 0;j < n;j++) {
                if (grid[i][j] == 1) {
                    d[i][j] = 0;// 陆地的距离为0
                    q.offer(new Status(0,i,j));
                } else {
                    d[i][j] = INF;// 海洋距离初始化为INF
                }
            }
        }
        // 
        while (!q.isEmpty()) {
            Status f = q.peek();
            q.poll();
            // 遍历四个方向
            for (int i = 0;i < 4;i++) {
                int nx = f.x + dx[i],ny = f.y + dy[i];
                if (!(nx >= 0 && nx <= n - 1 && ny >= 0 && ny <= n - 1)) {
                    continue;
                }
                // 新位置的权值大于应该有的权值,需要更新(nx,ny)处的权值
                if (f.v + 1 < d[nx][ny]) {
                    d[nx][ny] = f.v + 1;
                    q.offer(new Status(d[nx][ny],nx,ny));
                }
            }
        }
        // 遍历每一个海岸区域,求最大的距离(海洋与陆地)
        int ans = -1;
        for (int i = 0;i < n;i++) {
            for (int j = 0;j < n;j++) {
                if (grid[i][j] == 0) {
                    ans = Math.max(ans,d[i][j]);
                }
            }
        }
        return (ans == INF)? -1 : ans;
    }
}
- 多源BFS版本
class Solution {
    int MAX_N = 100 + 5;
    int INF = 1000000;
    // 位移辅助变量
    int[] dx = new int[]{-1,0,1,0};
    int[] dy = new int[]{0,1,0,-1};
    int n;
    int[][] d = new int[MAX_N][MAX_N];
    class Coordinate {
        int x, y;
        public Coordinate(int x,int y) {
            this.x = x;
            this.y = y;
        }
    };
    queue <Coordinate> q =  new LinkedList<Coordinate>();
    boolean[][] inq = new boolean[MAX_N][MAX_N];
    int maxDistance(int[][] grid) {
        this.n = grid.length;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) {
                    d[i][j] = 0;
                    q.offer(new Coordinate(i, j));
                    inq[i][j] = true;
                } else {
                    d[i][j] = INF;
                }
            }
        }
        while (!q.isEmpty()) {
            Coordinate f = q.peek(); q.poll(); inq[f.x][f.y] = false;
            for (int i = 0; i < 4; ++i) {
                int nx = f.x + dx[i], ny = f.y + dy[i];
                if (!(nx >= 0 && nx <= n - 1 && ny >= 0 && ny <= n - 1)) continue;
                if (d[nx][ny] > d[f.x][f.y] + 1) {
                    d[nx][ny] = d[f.x][f.y] + 1;
                    if (!inq[nx][ny]) {
                        q.offer(new Coordinate(nx, ny));
                        inq[nx][ny] = true;
                    }
                }
            }
        }
        int ans = -1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 0) ans = Math.max(ans, d[i][j]);
            }
        }
        return (ans == INF) ? -1 : ans;
    }
}
// 作者:LeetCode-Solution
// 链接:https://leetcode-cn.com/problems/as-far-from-land-as-possible/solution/di-tu-fen-xi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3、动态规划
考虑优化方法二中的「把陆地区域作为源点集、海洋区域作为目标点集,求最短路」的过程。我们知道对于每个海洋区域 (x, y)(x,y),离它最近的陆地区域到它的路径要么从上方或者左方来,要么从右方或者下方来。考虑做两次动态规划,第一次从左上到右下,第二次从右下到左上,记 f(x, y) 为 (x,y) 距离最近的陆地区域的曼哈顿距离。
- 第一阶段:从左上到右下
 $$
 f(x,y)=\begin{cases}
 0,& (x,y)是陆地\
 min{f(x-1,y),f(x,y-1)}+1, & (x,y)是海洋
 \end{cases}
 $$
- 第二阶段:从右下到左上
 $$
 f(x,y)=\begin{cases}
 0,& (x,y)是陆地\
 min{f(x+1,y),f(x,y+1)}+1, &(x,y)是海洋
 \end{cases}
 $$
class Solution {
    int MAX_N = 100 + 5;
    int INF = 1000000;
    
    int[][] f = new int[MAX_N][MAX_N];
    int n;
    int maxDistance(int[][] grid) {
        this.n = grid.length;
        // 初始化状态数组
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                f[i][j] = (a[i][j] == 1 ? 0 : INF);
            }
        }
        // 从左上到右下
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) continue;
                if (i - 1 >= 0) f[i][j] = Math.min(f[i][j], f[i - 1][j] + 1);
                if (j - 1 >= 0) f[i][j] = Math.min(f[i][j], f[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 + 1 < n) f[i][j] = Math.min(f[i][j], f[i + 1][j] + 1);
                if (j + 1 < n) f[i][j] = Math.min(f[i][j], f[i][j + 1] + 1);
            }
        }
        int ans = -1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 0) {
                    ans = Math.max(ans, f[i][j]);
                }
            }
        }
        if (ans == INF) return -1;
        else return ans;
    }
}
// 作者:LeetCode-Solution
// 链接:https://leetcode-cn.com/problems/as-far-from-land-as-possible/solution/di-tu-fen-xi-by-leetcode-solution/
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com
 
            