LC-1263. 推箱子(网格图BFS + DFS)

news2025/1/12 22:52:55

1263. 推箱子

难度困难105

「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。

游戏地图用大小为 m x n 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。

现在你将作为玩家参与游戏,按规则将箱子 'B' 移动到目标位置 'T'

  • 玩家用字符 'S' 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
  • 地板用字符 '.' 表示,意味着可以自由行走。
  • 墙用字符 '#' 表示,意味着障碍物,不能通行。
  • 箱子仅有一个,用字符 'B' 表示。相应地,网格上有一个目标位置 'T'
  • 玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
  • 玩家无法越过箱子。

返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1

示例 1:

img

输入:grid = [["#","#","#","#","#","#"],
             ["#","T","#","#","#","#"],
             ["#",".",".","B",".","#"],
             ["#",".","#","#",".","#"],
             ["#",".",".",".","S","#"],
             ["#","#","#","#","#","#"]]
输出:3
解释:我们只需要返回推箱子的次数。

示例 2:

输入:grid = [["#","#","#","#","#","#"],
             ["#","T","#","#","#","#"],
             ["#",".",".","B",".","#"],
             ["#","#","#","#",".","#"],
             ["#",".",".",".","S","#"],
             ["#","#","#","#","#","#"]]
输出:-1

示例 3:

输入:grid = [["#","#","#","#","#","#"],
             ["#","T",".",".","#","#"],
             ["#",".","#","B",".","#"],
             ["#",".",".",".",".","#"],
             ["#",".",".",".","S","#"],
             ["#","#","#","#","#","#"]]
输出:5
解释:向下、向左、向左、向上再向上。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 20
  • grid 仅包含字符 '.', '#', 'S' , 'T', 以及 'B'
  • grid'S', 'B''T' 各只能出现一个。

题解:https://leetcode.cn/problems/minimum-moves-to-move-a-box-to-their-target-location/solution/tong-su-yi-dong-dai-ma-dai-zhu-shi-java-mi4f6/

试想一下,如果箱子可以自己移动(见鬼),这道题目你会做吗?

这不就变成了一个简单的BFS吗?之所以用BFS而不是DFS,是因为使用BFS(层次化版本)可以一层一层向外扩展,从而轻易得到“最短移动步数”。

再思考,如果加入“箱子需要人从背后推动”的条件,会带来什么不同呢?

箱子的移动受到了限制——只有人可以到达箱子的背后时,箱子才能在这个特定方向进行移动。至于“人是否可以到达箱子的背后”,这个子问题又可以用一次DFS来解决。

由此,我们得到了第一个重要的思路:

以箱子的视角进行BFS(主问题),以人的视角进行DFS(子问题),后者是前者得以进行的前提。

想象一下,此时箱子正位于一个狭窄的“通道”内,这种情况下,人究竟是站在箱子的那一侧就尤为重要。换句话讲,箱子虽然位于同一位置,但人的位置不同,箱子其实仍处于不同的状态(请仔细琢磨“状态”这个用词)。

由此,引出了第二个重要的思路:

箱子的状态包含两个信息,箱子的位置、箱子的来源(它刚刚是以什么样的方向被推来的)。

而我们为什么要纠结于箱子的状态?

因为箱子在BFS时需要设置visited数组来防止重复(实际上防止死循环),而是否发生重复的依据正是箱子的状态。从代码的角度看,我们熟悉的visited数组长这个样子:boolean[][],而现在它变成了这样:boolean[][][4],4是指方向信息。

class Solution {
    //【BFS+DFS】以箱子的视角进行BFS,以人的视角进行DFS,后者作为前者得以进行的前提
    private final static int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int minPushBox(char[][] grid) {
        int m = grid.length, n = grid[0].length;
        // 遍历一次,找出箱子起点/终点,人的初始位置
        int startX = -1;
        int startY = -1;
        int targetX = -1;
        int targetY = -1;
        int personX = -1;
        int personY = -1;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 'B') {
                    startX = i;
                    startY = j;
                }
                if (grid[i][j] == 'T') {
                    targetX = i;
                    targetY = j;
                    grid[i][j] = '.';
                }
                if (grid[i][j] == 'S') {
                    personX = i;
                    personY = j;
                    grid[i][j] = '.';
                }
            }
        }
        // 初始化队列,加入元素以启动BFS
        boolean[][][] visited = new boolean[m][n][4];
        Queue<Box> queue = new LinkedList<>();
        for(int i = 0; i < 4; i++){
            int[] d = directions[i];
            if (personCanReach(grid, m, n, personX, personY, startX - d[0], startY - d[1], new boolean[m][n])) {
                queue.add(new Box(startX, startY, i));
                visited[startX][startY][i] = true;
            }
        }

        // 以箱子的视角开始BFS
        int step = 0;
        while(!queue.isEmpty()){
            int size = queue.size();
            while(size-- > 0){
                Box box = queue.poll();
                grid[box.x][box.y] = 'B'; // 当前箱子的位置(人不能过)
                // 人在哪推的这一步? 当箱子移动时需要判断人能否走到推动位置推动箱子
                personX = box.x - directions[box.from][0];
                personY = box.y - directions[box.from][1];
                if(box.x == targetX && box.y == targetY)
                    return step;
                // BFS网格图遍历
                for(int i = 0; i < 4; i++){
                    int[] d = directions[i];
                    int nextX = box.x + d[0];
                    int nextY = box.y + d[1];
                    // 人是否能绕到箱子的后面?  (box.x, box.y)箱子当前位置,(box.x+d[0], box.y+d[1])箱子目标位置,(box.x-d[0], box.y-d[1])人推动箱子到目标位置的位置
                    if (!personCanReach(grid, m, n, personX, personY, box.x - d[0], box.y - d[1], new boolean[m][n])) {
                        continue;
                    }
                    // 箱子的下个位置是否合法?
                    if (!isValid(grid, m, n, nextX, nextY)) {
                        continue;
                    }
                    // 箱子的下一个状态是不是重复了?
                    if(visited[nextX][nextY][i])
                        continue;
                    queue.add(new Box(nextX, nextY, i));
                    visited[nextX][nextY][i] = true;
                }
                grid[box.x][box.y] = '.'; // 重置箱子的位置为地面,后面进行BFS还会弹出一个箱子位置
            }
            step += 1;
        }
        return -1;
    }

    // 人是否可以某一位置(startX, startY)到达另一位置(targetX, targetY)
    private boolean personCanReach(char[][] grid, int m, int n, int startX, int startY, int targetX, int targetY, boolean[][] visited) {
        if (startX == targetX && startY == targetY) {
            return true;
        }
        visited[startX][startY] = true;
        for (int[] direction : directions) {
            int nextX = startX + direction[0];
            int nextY = startY + direction[1];
            if (isValid(grid, m, n, nextX, nextY) && !visited[nextX][nextY]) {
                if (personCanReach(grid, m, n, nextX, nextY, targetX, targetY, visited)) {
                    return true;
                }
            }
        }
        return false;
    }

    // 某位置是否可以踏足
    private boolean isValid(char[][] grid, int m, int n, int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '.';
    }

    // 内部类,记录箱子位置、从哪个方向推过来的
    class Box{
        int x, y, from;
        public Box(int x, int y, int from){
            this.x = x;
            this.y = y;
            this.from = from;
        }
    }
}

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

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

相关文章

Sleuth和zipkin

1、Sleuth是什么 为什么会出现这个技术&#xff1f;要解决哪些问题&#xff1f; 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会形成一条复杂的分布式服务调用链路&am…

2.RabbitMQ

RabbitMQ 1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&am…

查询文件路径

1 问题 如何利用Java来查询文件的路径&#xff1f; 2 方法 1首先在类中利用main函数调用所有文件的和目录的代码。 2 然后开始写查询展示所有文件和目录的方法&#xff08;运用了递归的思想&#xff09; import java.io.File;import java.util.Arrays;import java.util.Scanner…

ETO、MTO、ATO与MTS(按单设计、按单生产、按单装配和库存生产)

按照企业组织生产的特点&#xff0c;可以把制造企业划分为ETO、ATO、MTO与MTS&#xff08;按单设计、按单装配、按单生产和库存生产&#xff09;四种生产类型。 按单设计&#xff08;Engineer To Order&#xff0c;ETO&#xff09;   在这种生产类型下&#xff0c;一种产品在…

【Sentinel源码分析】

Sentinel源码分析 Sentinel源码分析1.Sentinel的基本概念1.1.ProcessorSlotChain1.2.Node1.3.Entry1.3.1.自定义资源1.3.2.基于注解标记资源 1.4.Context1.4.1.什么是Context1.4.2.Context的初始化1.4.2.1.自动装配1.4.2.2.AbstractSentinelInterceptor1.4.2.3.ContextUtil 2.P…

软件工具 | Python调用运筹优化求解器(一):以CVRPVRPTW为例

目录 1. 引言2. 求解器介绍3. 基础语言3.1 创建模型3.2 添加变量3.3 添加目标函数3.4 添加约束3.5 设置参数3.6 求解 4. 数学模型4.1 [CVRP数学模型](https://mp.weixin.qq.com/s/DYh-5WkrYxk1gCKo8ZjvAw)4.2 [VRPTW数学模型](https://mp.weixin.qq.com/s/tF-ayzjpZfuZvelvItue…

【网络】4万字细品TCP协议

文章目录 TCP协议关于UDP和TCP的优缺点 TCP协议格式4位首部长度可靠性的理解确认应答的工作方式序号和确认序号如何保证报文的顺序呢确认序号的特点:为什么有两套序号总结:序列号的原理 16位窗口大小TCP的缓冲区为什么TCP叫做传输控制协议缓冲区存在的意义 窗口大小 6个标记位为…

python-数据类型

Python基础数据类型(int,str,bool)、格式化输出、程序交换 捕翼 于 2020-07-28 21:29:14 发布 2179 收藏 3 分类专栏&#xff1a; Python3.6.5 版权 Python3.6.5 专栏收录该内容 33 篇文章0 订阅 订阅专栏 文章目录 一、程序交互 二、格式化输出 数据类型&#xff1a; 三、…

四维轻云地理空间数据在线管理软件能够在线管理哪些数据?

四维轻云是一款地理空间数据在线管理软件&#xff0c;支持各类地理空间数据的在线管理、浏览及分享&#xff0c;用户可不受时间地点限制&#xff0c;随时随地查看各类地理空间数据。软件还具有项目管理、场景搭建、素材库等功能模块&#xff0c;支持在线协作管理&#xff0c;便…

Spring Boot 如何让你的 bean 在其他 bean 之前完成加载 ?

问题 今天有个小伙伴给我出了一个难题&#xff1a;在 SpringBoot 中如何让自己的某个指定的 Bean 在其他 Bean 前完成被 Spring 加载&#xff1f;我听到这个问题的第一反应是&#xff0c;为什么会有这样奇怪的需求&#xff1f; Talk is cheap&#xff0c;show me the code&am…

精力管理金字塔

精力管理金字塔 由协和医学院的张遇升博士在《掌控精力&#xff1a;不疲惫的身心管理术》一书中提出&#xff0c;分层次对精力管理提出了解析和有效的建议。 模型介绍 精力管理是一个可以学会的技能&#xff0c;学会了科学的精力管理方法&#xff0c;就能使自己的精力越来越好…

实在智能RPA首推集约式“智能门户超自动化办公"新模式,加速司法、政企数字化升级

随着数字化和智能化的快速发展&#xff0c;数字技术已经深入到各个行业和领域。实在智能基于数字员工在行业的深厚理解和丰富的实践经验&#xff0c;打造一站式的智能化统一平台——智能门户&#xff0c;打破了技术壁垒和系统数据之间的割裂感&#xff0c;实现政府、企业内部信…

2023最新版本Camtasia电脑录屏软件好不好用?

在当今数字化时代&#xff0c;屏幕录制成为了许多用户制作教学视频、演示文稿、游戏攻略等内容的首选。本文将为您介绍几款常用的电脑录屏软件&#xff0c;包括Camtasia、OBS Studio、Bandicam等&#xff0c;并对其进行功能和用户体验方面的比较&#xff0c;同时给出10款电脑录…

PHP语言技术开发的手术麻醉管理系统源码

手术麻醉管理系统用于各个手术室和麻醉科&#xff0c;接受医生工作站、护士工作站发送过来的手术申请单 手术麻醉管理系统(DORIS)是应用于医院手术室、麻醉科室的计算机软件系统。该系统针对整个围术期&#xff0c;对病人进行全程跟踪与信息管理&#xff0c;自动集成病人HIS、…

上海约瑟 HJZ-J913静态中间继电器 导轨安装 触点容量16A/250VAC

品牌&#xff1a;JOSEF约瑟&#xff0c;型号&#xff1a;HJZ-J913&#xff0c;名称&#xff1a;静态中间继电器&#xff0c;额定电压&#xff1a;48220VDC&#xff1b;48415VAC&#xff0c;触点容量&#xff1a;250V/5A&#xff0c;功率消耗&#xff1a;≤5W&#xff0c;动作时…

零入门kubernetes网络实战-31->基于bridge+veth pair+MASQUERADE技术实现内网可以访问外网

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本篇文章我们使用nat技术来实现bridge管理的网络能够访问外网。 1、测试环境介绍 两台centos虚拟机 # 查看操作系统版本 cat /etc/centos-release # 内核…

(5)Qt—ui常用类

目录 1. QString 字符串类* 2. 容器类 2.1 顺序容器 QList 1. QString 字符串类* QString是Qt中的字符串类&#xff0c;与C和C不同的是&#xff0c;Qt的字符串使用Unicode编码。每一个字符使用一个16位的QChar&#xff0c;而不是之前8位的char&#xff0c;所以Qt处理中文没有问…

基于ssm医药药品管理系统

开发工具eclipse&#xff0c;jdk1.8 数据库mysql5.7,tomcat8 技术&#xff1a;springmvcspringmybatis(ssm) 主要功能如下&#xff1a; ①登录系统&#xff1a;管理员需要输入正确的用户名和密码来登录系统&#xff0c;从而完成各类信息的管理工作&#xff1b; ②信息查询…

前端007_类别模块_列表功能

1、需求分析 类别模块主要文章类别进行管理,首先实现类别列表功能,包含数据列表、分页、条件查询 。 2、Mock模拟接口数据 请求URL: /article/category/search请求方式: post描述: 文章类别分页条件查询列表mock.js 配置: 带分页功能,每页显示20条{"code": 2…

【网络取证篇】宝塔面板server和panel的目录功能说明

【网络取证篇】宝塔面板server和panel的目录功能说明 宝塔面板的目录结构在不同的情况下可能有所不同—【蘇小沐】 文章目录 【网络取证篇】宝塔面板server和panel的目录功能说明1、宝塔优点&#xff1a; &#xff08;一&#xff09;宝塔面板/www/server主要目录&#xff08;二…