LeetCode 2258. 逃离火灾:BFS

news2024/11/28 12:54:15

【LetMeFly】2258.逃离火灾

力扣题目链接:https://leetcode.cn/problems/escape-the-spreading-fire/

给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid ,它表示一个网格图。每个格子为下面 3 个值之一:

  • 0 表示草地。
  • 1 表示着火的格子。
  • 2 表示一座墙,你跟火都不能通过这个格子。

一开始你在最左上角的格子 (0, 0) ,你想要到达最右下角的安全屋格子 (m - 1, n - 1) 。每一分钟,你可以移动到 相邻 的草地格子。每次你移动 之后 ,着火的格子会扩散到所有不是墙的 相邻 格子。

请你返回你在初始位置可以停留的 最多 分钟数,且停留完这段时间后你还能安全到达安全屋。如果无法实现,请你返回 -1 。如果不管你在初始位置停留多久,你 总是 能到达安全屋,请你返回 109 。

注意,如果你到达安全屋后,火马上到了安全屋,这视为你能够安全到达安全屋。

如果两个格子有共同边,那么它们为 相邻 格子。

 

示例 1:

输入:grid = [[0,2,0,0,0,0,0],[0,0,0,2,2,1,0],[0,2,0,0,1,2,0],[0,0,2,2,2,0,2],[0,0,0,0,0,0,0]]
输出:3
解释:上图展示了你在初始位置停留 3 分钟后的情形。
你仍然可以安全到达安全屋。
停留超过 3 分钟会让你无法安全到达安全屋。

示例 2:

输入:grid = [[0,0,0,0],[0,1,2,0],[0,2,0,0]]
输出:-1
解释:上图展示了你马上开始朝安全屋移动的情形。
火会蔓延到你可以移动的所有格子,所以无法安全到达安全屋。
所以返回 -1 。

示例 3:

输入:grid = [[0,0,0],[2,2,0],[1,2,0]]
输出:1000000000
解释:上图展示了初始网格图。
注意,由于火被墙围了起来,所以无论如何你都能安全到达安全屋。
所以返回 109

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 300
  • 4 <= m * n <= 2 * 104
  • grid[i][j] 是 0 ,1 或者 2 。
  • grid[0][0] == grid[m - 1][n - 1] == 0

方法一:二分 + BFS

首先以所有的🔥为起点开始广度优先搜索,这样我们就能得到“火焰燃烧图”(🔥燃烧到某个坐标所需耗时)。

接着可以二分“👱的开局等待时长”。假设开局等待时间为 t t t,那么就从时间 t t t开始对👱能到达的位置进行广度优先搜索。

在对👱的广搜过程中:

  • 若搜索到了“安全屋”的位置:若“👱的到达耗时小于等于🔥的到达耗时”,则表示👱能等待时间 t t t后再出发
  • 否则(非安全屋位置):若“👱的到达耗时小于🔥的到达耗时”,则表示人能到达该位置

以上,即可。

  • 时间复杂度 O ( m n log ⁡ m n ) O(mn\log mn) O(mnlogmn),其中 s i z e ( g r i d ) = m × n size(grid)=m\times n size(grid)=m×n
  • 空间复杂度 O ( m n ) O(mn) O(mn)

AC代码

C++
class Solution {
private:
    int m, n;
    int direction[4][2] = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
    vector<vector<int>> fireTime;
    void debug(vector<vector<int>>& v) {
        for (auto& t : v) {
            for (auto& tt : t) {
                cout << tt << ' ';
            }
            cout << endl;
        }
    }

    void bfsFire(vector<vector<int>>& grid) {  // 计算火燃烧到每个位置时所需耗时并存入fireTime
        vector<vector<int>> graph = grid;
        fireTime = vector<vector<int>>(m, vector<int>(n, 1e9));
        queue<pair<int, int>> q;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (graph[i][j] == 1) {
                    q.push({i, j});
                    fireTime[i][j] = 0;
                }
            }
        }
        while (q.size()) {
            auto [x, y] = q.front();
            q.pop();
            for (int d = 0; d < 4; d++) {
                int tx = x + direction[d][0];
                int ty = y + direction[d][1];
                if (tx >= 0 && tx < m && ty >= 0 && ty < n && !graph[tx][ty]) {
                    graph[tx][ty] = 1;
                    fireTime[tx][ty] = fireTime[x][y] + 1;
                    q.push({tx, ty});
                }
            }
        }
    }

    bool check(vector<vector<int>>& grid, int t) {  // 其实是bfsPeople
        vector<vector<int>> peopleTime(m, vector<int>(n, 0)), graph(grid);
        peopleTime[0][0] = t;
        queue<pair<int, int>> q;
        q.push({0, 0});
        graph[0][0] = 2;
        while (q.size()) {
            auto [x, y] = q.front();
            q.pop();
            for (int d = 0; d < 4; d++) {
                int tx = x + direction[d][0];
                int ty = y + direction[d][1];
                int toTime = peopleTime[x][y] + 1;
                if (tx >= 0 && tx < m && ty >= 0 && ty < n && !graph[tx][ty]) {
                    graph[tx][ty] = 2;
                    if (tx == m - 1 && ty == n - 1 && toTime <= fireTime[m - 1][n - 1]) {
                        return true;
                    }
                    if (toTime < fireTime[tx][ty]) {
                        peopleTime[tx][ty] = toTime;
                        q.push({tx, ty});
                    }
                }
            }
        }
        return false;
    }
public:
    int maximumMinutes(vector<vector<int>>& grid) {
        m = grid.size(), n = grid[0].size();
        bfsFire(grid);
        int l = 0, r = n * m;
        int ans = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (check(grid, mid)) {
                ans = mid;
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        return ans >= n * m ? 1e9 : ans;
    }
};
Python
# from typing import List
# from copy import deepcopy

class Solution:
    def __init__(self) -> None:
        self.direction = [[-1, 0], [1, 0], [0, -1], [0, 1]]
    
    def bfsFire(self, grid: List[List[int]]) -> None:
        fireTime = [[int(1e9)] * self.n for _ in range(self.m)]
        graph = deepcopy(grid)
        q = []
        for i in range(self.m):
            for j in range(self.n):
                if graph[i][j] == 1:
                    q.append((i, j))
                    fireTime[i][j] = 0
        while q:
            x, y = q[0]
            q = q[1:]
            for dx, dy in self.direction:
                tx, ty = x + dx, y + dy
                if tx >= 0 and tx < self.m and ty >= 0 and ty < self.n and not graph[tx][ty]:
                    q.append((tx, ty))
                    fireTime[tx][ty] = fireTime[x][y] + 1
                    graph[tx][ty] = 1
        self.fireTime = fireTime
    
    def check(self, grid: List[List[int]], t: int) -> bool:
        if t == 4:
            print(self.fireTime)
        peopleTime = [[0] * self.n for _ in range(self.m)]
        graph = deepcopy(grid)
        q = []
        q.append((0, 0))
        graph[0][0] = 2
        peopleTime[0][0] = t
        while q:
            x, y = q[0]
            q = q[1:]
            thisTime = peopleTime[x][y] + 1
            for dx, dy in self.direction:
                tx, ty = x + dx, y + dy
                if tx >= 0 and tx < self.m and ty >= 0 and ty < self.n and not graph[tx][ty]:
                    graph[tx][ty] = 2
                    if tx == self.m - 1 and ty == self.n - 1 and thisTime <= self.fireTime[-1][-1]:
                        return True
                    if thisTime < self.fireTime[tx][ty]:
                        peopleTime[tx][ty] = thisTime
                        q.append((tx, ty))
        return False

    def maximumMinutes(self, grid: List[List[int]]) -> int:
        self.m, self.n = len(grid), len(grid[0])
        self.bfsFire(grid)
        l, r = 0, self.m * self.n
        ans = -1
        while l <= r:
            mid = (l + r) // 2
            if self.check(grid, mid):
                ans = mid
                l = mid + 1
            else:
                r = mid - 1
        return int(1e9) if ans >= self.m * self.n else ans

if __name__ == '__main__':
    print(Solution().maximumMinutes(
        [[0,2,0,0,0,0,0],
         [0,0,0,2,2,1,0],
         [0,2,0,0,1,2,0],
         [0,0,2,2,2,0,2],
         [0,0,0,0,0,0,0]])
    )
    """
    [[6, ∞, 4, 3, 2, 1, 2],
     [5, 4, 3, ∞, ∞, 0, 1],
     [6, ∞, 2, 1, 0, ∞, 2],
     [7, 8, ∞, ∞, ∞, 14, ∞],
     [8, 9, 10, 11, 12, 13, 14]]
    """

方法二:数次BFS(无代码,可忽略)

其实这道题特殊的一点只有“安全屋”,只有安全屋这里🔥和👱可以同时到达。其他位置都必须保证👱比🔥严格地优先到达。

怎么到安全屋呢?要么从安全屋的左边,要么从安全屋的上面。因此先BFS一下得到🔥的“燃烧耗时图”,再按从 0 0 0时刻出发BFS👱。

最后判断一下安全屋及其左上两个位置👱🔥的到达时间,即可推断出👱在起点最多待多久。

2 15 > 2 × 1 0 4 2^{15}>2\times10^4 215>2×104,故方法一中也不会二分太多次。

同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/134331955

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1192463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

打印流详解

概述 作用&#xff1a;打印流可以实现方便、高效的打印数据到文件中去。 高效体现在用到了缓冲流&#xff1a; public PrintStream(OutputStream out, boolean autoFlush, Charset charset) {super(out);this.autoFlush autoFlush;this.charOut new OutputStreamWriter(thi…

西门子S7-1200PLC混合通信编程(ModbusTcp和UDP通信)

S7-1200PLC的MODBUS-TCP通信 西门子PLC ModbusTcp通信访问网关后从站(SCL语言轮询状态机)-CSDN博客文章浏览阅读305次。西门子PLC的ModbusTcp通信在专栏已有很多文章介绍,所不同的是每个项目的通信需求都略有不同,今天我们以访问网关后的三个从站数据来举例,给出轮询的推荐…

【带头学C++】----- 三、指针章 ---- 3.10 函数指针(补充基础知识)

1.函数指针 1.1 函数的返回值类型为指针类型 将函数内部的合法地址通过返回值 返回给函数外部使用 注意:函数不要返回普通局部变量的地址 分析&#xff1a; 在这段代码中&#xff0c;函数getAddr()返回一个指向局部变量data地址&#xff08;作用域是函数内部&#xff09;的指…

单链表(5)

判空函数 *一进函数先断言 获取数据结点的个数函数 如图&#xff0c;p->nextNULL就跳出的话&#xff0c;当前p->data就没算上。 现在来测试一下 同样在空表时也调用一下 还有这样写的&#xff0c;出来的结果也是一样的&#xff0c;它也算是对的——但是&#xff0c;这是…

数据结构与算法—冒泡排序快速排序

目录 一、交换排序 二、冒泡排序 时间复杂度 三、快速排序 1、三种一次划分操作 Hoare法 挖洞法 前后指针法 三种方法总结&#xff1a; 2、改进划分效率 3、递归实现快速排序 4、非递归实现快速排序 栈的函数&#xff1a; 非递归排序函数&#xff1a; 5、时…

tensorboard玩耍手册

from torch.utils.tensorboard import SummaryWriter 来自官网的示例&#xff1a;TensorBoard: TensorFlow可视化 查看 event 文件 文件名含义 根据参考[1]文档的来说&#xff1a;文件名含义如下&#xff1a; <WORKING_DIR>/runs/<DATETIME>_<MACHINE_NAME>…

YOLOv8-Seg改进: 分割小目标系列篇 | 多头分割器,提升分割小目标和弱小分割精度

🚀🚀🚀本文改进:YOLOv8-Seg有3个分割头,能够多尺度分割,但对小目标分割可能存在分割能力不佳的现象,因此添加一个微小物体的分割头,能够大量涨点,map提升明显,特别是在处理低分辨率图像和分割小目标等更困难的任务时。 🚀🚀🚀多头分割器 分割小目标检测首选…

【Unity程序小技巧】如何消除多次Destory带来的性能消耗

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

如何优化负载均衡?一文讲懂

当web应用程序增长到单服务器无法承受的地步&#xff0c;企业就面临着优化负载均衡的需求。简而言之&#xff0c;企业需要实现流量重定向&#xff0c;就需要从业务可靠性的需求出发&#xff0c;寻找一套可行的负载均衡方案&#xff0c;那么常用的负载均衡方案有哪些&#xff1f…

Jenkins CICD过程常见异常

1 Status [126] Exception when publishing, exception message [Exec exit status not zero. Status [126] 1.1 报错日志 SSH: EXEC: STDOUT/STDERR from command [/app/***/publish.sh] ... bash: /app/***/publish.sh: Permission denied SSH: EXEC: completed after 200…

【ElasticSearch系列-06】Es集群架构的搭建以及集群的核心概念

ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…

uniapp vue2 vuex 持久化

1.vuex的使用 一、uniapp中有自带vuex插件&#xff0c;直接引用即可 二、在项目中新建文件夹store,在main.js中导入 在根目录下新建文件夹store,在此目录下新建index.js文件 index.js import Vue from vueimport Vuex from vuexVue.use(Vuex)const store new Vuex.Store(…

客户服务质量提升的三种思路

客户服务质量是企业在市场竞争中立于不败之地的重要因素之一&#xff0c;优秀的客户服务不仅可以提高客户满意度&#xff0c;还可以提高客户黏度和回头率。随着经济的发展&#xff0c;客户服务行业也在不断发展壮大。在这个竞争激烈的行业中&#xff0c;企业如何提高客户服务质…

如何自己实现一个丝滑的流程图绘制工具(九) 自定义连接线

背景 产品又有更近的想法了&#xff0c;bpmn-js的连接线你用的时候是看不到的&#xff0c;也就是你从左侧点击连接线的没有线随鼠标移动. 但是产品想要看得见的连接线移动拖拽。 咩有办法&#xff0c;不能换框架&#xff0c;那就只能自己实现啦&#xff01; 思路&#xff1a; …

多维详述MediaBox互动直播AUI Kit低代码开发方案

本专栏将分享阿里云视频云MediaBox系列技术文章&#xff0c;深度剖析音视频开发利器的技术架构、技术性能、开发能效和最佳实践&#xff0c;一起开启音视频的开发之旅。本文为MediaBox最佳实践篇&#xff0c;重点从互动直播AUI Kit的核心能力、技术架构、快速集成等方面&#x…

【Word自定义配置,超简单,图文并茂】自定义Word中的默认配置,比如标题大小与颜色(参考科研作图配色),正文字体等

▚ 01 自定义样式Styles中的默认标题模板 &#x1f4e2;自定义标题的显示效果&#xff0c;如下图所示&#xff1a; 1.1 自定义标题的模板Normal.dotm 1.1.1 选择所需修改的标题 新建一个空白Word文档&#xff0c;依次选择菜单栏的开始Home&#xff0c;样式Styles&#xff0c;…

2023最新版本 FreeRTOS教程 -10-事件组(通过5种情况快速上手)

事件组对应单个事件触发或多个事件同时触发的场景 创建事件组函数 EventGroupHandle_t xEventGroupCreate( void );删除事件组函数 void vEventGroupDelete( EventGroupHandle_t xEventGroup )设置事件 在任务中使用xEventGroupSetBits() 在中断中使用xEventGroupSetBits…

注解和反射实现Excel导入导出

目录 使用实例 定义三个注解 /*** 设置允许导出*/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface EnableExport {String fileName();} /*** 设置该字段允许导出* 并且可以设置宽度*/ @Target(ElementType.FIELD) @Retention(RetentionP…

高级算法复习

时间代价 主定理 递归树 排序 贪心算法 贪心选择性&#xff08;Greedy-choice property&#xff09;&#xff1a; 通过做出局部最优&#xff08;贪婪&#xff09;选择&#xff0c;可以得出全局最优解——这是贪心算法可行的第一个基本要素&#xff0c;也是贪心算法与动态规划…

使用Nginx和Spring Gateway为SkyWalking的增加登录认证功能

文章目录 1、使用Nginx增加认证。2、使用Spring Gateway增加认证 SkyWalking的可视化后台是没有用户认证功能的&#xff0c;默认下所有知道地址的用户都能访问&#xff0c;官网是建议通过网关增加认证。 本文介绍通过Nginx和Spring Gateway两种方式 1、使用Nginx增加认证。 生…