在这里插入图片描述

🚀 算法题 🚀

🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀
🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨
🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🌲 恭喜你发现一枚宝藏博主,赶快收入囊中吧🌻
🌲 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯

🚀 算法题 🚀

在这里插入图片描述
在这里插入图片描述

🚩 题目链接

⛲ 题目描述

给你一个下标从 0 开始、大小为 n x n 的二维矩阵 grid ,其中 (r, c) 表示:

  • 如果 grid[r][c] = 1 ,则表示一个存在小偷的单元格
  • 如果 grid[r][c] = 0 ,则表示一个空单元格

你最开始位于单元格 (0, 0) 。在一步移动中,你可以移动到矩阵中的任一相邻单元格,包括存在小偷的单元格。

矩阵中路径的 安全系数 定义为:从路径中任一单元格到矩阵中任一小偷所在单元格的 最小 曼哈顿距离。

返回所有通向单元格 (n - 1, n - 1) 的路径中的 最大安全系数 。

单元格 (r, c) 的某个 相邻 单元格,是指在矩阵中存在的 (r, c + 1)、(r, c - 1)、(r + 1, c) 和 (r - 1, c) 之一。

两个单元格 (a, b) 和 (x, y) 之间的 曼哈顿距离 等于 | a - x | + | b - y | ,其中 |val| 表示 val 的绝对值。

示例 1:
在这里插入图片描述
输入:grid = [[1,0,0],[0,0,0],[0,0,1]]
输出:0
解释:从 (0, 0) 到 (n - 1, n - 1) 的每条路径都经过存在小偷的单元格 (0, 0) 和 (n - 1, n - 1) 。

示例 2:
在这里插入图片描述
输入:grid = [[0,0,1],[0,0,0],[0,0,0]]
输出:2
解释:上图所示路径的安全系数为 2:

  • 该路径上距离小偷所在单元格(0,2)最近的单元格是(0,0)。它们之间的曼哈顿距离为 | 0 - 0 | + | 0 - 2 | = 2 。
    可以证明,不存在安全系数更高的其他路径。

示例 3:
在这里插入图片描述
输入:grid = [[0,0,0,1],[0,0,0,0],[0,0,0,0],[1,0,0,0]]
输出:2
解释:上图所示路径的安全系数为 2:

  • 该路径上距离小偷所在单元格(0,3)最近的单元格是(1,2)。它们之间的曼哈顿距离为 | 0 - 1 | + | 3 - 2 | = 2 。
  • 该路径上距离小偷所在单元格(3,0)最近的单元格是(3,2)。它们之间的曼哈顿距离为 | 3 - 3 | + | 0 - 2 | = 2 。
    可以证明,不存在安全系数更高的其他路径。

提示:

  • 1 <= grid.length == n <= 400
  • grid[i].length == n
  • grid[i][j] 为 0 或 1
  • grid 至少存在一个小偷

🌟 求解思路&实现代码&运行结果


🥦 二分答案 + 多源 BFS
原题题意

给定一个 n × n 的二维网格 grid:

  • grid[i][j] = 1:该位置存在小偷;
  • grid[i][j] = 0:空白可通行格子;

需要寻找一条从左上角 (0,0) 到右下角 (n-1,n-1) 的路径,仅能上下左右移动。

安全系数定义

安全系数定义:一条路径的安全系数 = 路径中所有格子,到最近小偷距离的最小值。
目标:求出所有可行路径中,最大的安全系数。

边界限制
  • 网格尺寸 1 ≤ n ≤ 400;
  • 起点 / 终点若本身是小偷,直接返回 0。
🥦 核心逻辑拆解

整体分为两大模块:多源 BFS 预处理距离矩阵 + 二分答案 + BFS 校验可行性

模块 1:多源 BFS 预处理每个格子到最近小偷的最短距离
  1. 所有小偷作为同步起点,全部入队;
  2. 逐层向外扩散,每个格子第一次被访问时记录距离,该值就是到最近小偷的曼哈顿最短距离;
  3. 生成dist[i][j] 矩阵,后续直接取用,无需重复计算距离。
模块 2:二分答案寻找最大安全系数
  1. 二分范围:安全系数最小为 0,最大为 2n(网格对角线极限距离);
  2. 二分中点 mid 代表我们猜测的安全下限:能否存在一条起点到终点的路径,路径上每一格 dist[i][j] ≥ mid;
  3. 校验逻辑(BFS 连通性):
  • 起点距离小于 mid 直接不合法;
  • 只走距离≥mid 的格子,看能否连通终点;
  1. 校验成功:说明存在更优解,左边界右移,记录当前答案;校验失败:安全值过大,右边界左移;
  2. 二分结束后保存的最大值即为最终结果。
整体思路总结

先用多源 BFS 一次性算出全网格最短安全距离,再用二分快速缩小目标值,搭配 BFS 做连通性判断,规避暴力 DFS 枚举所有路径的指数级超时问题。

🥦 实现代码
class Solution {
    int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
    int n;
    int[][] dist;

    public int maximumSafenessFactor(List<List<Integer>> grid) {
        n = grid.size();
        dist = new int[n][n];
        for(int[] row : dist) Arrays.fill(row, -1);
        Queue<int[]> q = new LinkedList<>();

        // 多源BFS初始化:所有小偷入队
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(grid.get(i).get(j) == 1){
                    dist[i][j] = 0;
                    q.offer(new int[]{i, j});
                }
            }
        }

        // 多源BFS计算每个格子到最近小偷最短距离
        while(!q.isEmpty()){
            int[] cur = q.poll();
            int x = cur[0], y = cur[1];
            for(int[] d : dirs){
                int nx = x + d[0], ny = y + d[1];
                if(nx >=0 && nx < n && ny >=0 && ny < n && dist[nx][ny] == -1){
                    dist[nx][ny] = dist[x][y] + 1;
                    q.offer(new int[]{nx, ny});
                }
            }
        }

        // 二分安全系数
        int l = 0, r = n + n;
        int ans = 0;
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(check(mid)){
                ans = mid;
                l = mid + 1;
            }else{
                r = mid - 1;
            }
        }
        return ans;
    }

    // 判断是否存在路径,所有格子dist >= limit
    boolean check(int limit){
        if(dist[0][0] < limit) return false;
        boolean[][] vis = new boolean[n][n];
        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[]{0, 0});
        vis[0][0] = true;

        while(!q.isEmpty()){
            int[] cur = q.poll();
            int x = cur[0], y = cur[1];
            if(x == n-1 && y == n-1) return true;
            for(int[] d : dirs){
                int nx = x + d[0], ny = y + d[1];
                if(nx >=0 && nx < n && ny >=0 && ny < n && !vis[nx][ny] && dist[nx][ny] >= limit){
                    vis[nx][ny] = true;
                    q.offer(new int[]{nx, ny});
                }
            }
        }
        return false;
    }

    public int getDistance(int a, int b, int x, int y) {
        return Math.abs(a - x) + Math.abs(b - y);
    }
}

🥦 关键原理说明
多源 BFS 原理
  1. 普通单源 BFS 只有一个起点逐层扩散;多源 BFS 将所有小偷作为初始起点同时入队,同步向外逐层遍历。
  2. 每个格子第一次被队列访问时,必然来自距离它最近的小偷,直接赋值距离,一次遍历完成全网格最短距离计算。
  3. 对比暴力遍历所有小偷求曼哈顿距离,时间复杂度大幅降低。
二分答案原理
  1. 题目求 “路径最小值的最大值”,是典型二分答案题型:
  2. 安全系数存在单调性:若安全值 k 可行,则所有小于 k 的值一定可行;若 k 不可行,所有大于 k 的值均不可行;
  3. 利用单调性二分快速锁定最大合法值,避免暴力枚举所有安全系数。
BFS 连通校验原理
  1. 二分得到目标安全下限后,只需判断网格中是否存在连通起点终点的通路,且通路所有格子距离小偷都不低于该下限。
  2. BFS适合网格连通性判断,不会出现 DFS 递归深度溢出问题。
为什么不能暴力 DFS 枚举所有路径

网格最大 400×400,路径数量呈指数级增长,暴力回溯 DFS 会直接超时,无法通过中等规模测试用例。

🥦 运行效率
时间复杂度

多源 BFS 预处理:(O(n^2)),每个格子仅入队、出队一次;
二分循环:二分区间最大为 2n,循环次数 (O(logn));
每次二分内 BFS 校验:(O(n^2));
总复杂度:(O(n^2 * logn))

空间复杂度

(O(n^2)),用于存储距离矩阵、访问标记数组、BFS 队列,400×400 网格内存占用可控,无栈溢出风险。

💬 共勉

最后,我想和大家分享一句一直激励我的座右铭,希望可以与大家共勉!

在这里插入图片描述

在这里插入图片描述

更多推荐