【LeetCode困难】1263. 推箱子

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

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

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

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

示例 1:
在这里插入图片描述
输入: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’ 各只能出现一个。

这道题和上周华为暑期实习笔试的第三题有点相似,但个人感觉这道题难度更大。首先给这道题扣个帽子,属于一道搜索,但是不同于一般的搜索,这道题的搜索过程明显要麻烦一些。先说一下自己之前的错误思路:
①将整个问题分解为两个问题:人如何到达箱子边下以及箱子如何再到达终点。
②以人为起点进行广搜,当和箱子相遇之后就合为一体。

对于第一个思路,错误之处在于这是一个整体考虑的问题,并不是单独拆分就可以实现的,整体的最优值并不一定完全等于拆分后的最优值。而对于第二个思路,完全是读错了题目,题目要求的是,要计算推动箱子的次数,人推一下箱子之后,完全可以离开箱子跑到另一个位置来推。

最近一直在干组里的活,没时间仔细研究这道题,所以参考题解写了一版自己的代码。根据官方给的题解,这道题可以看作是一个复杂状态下的广搜,为什么说复杂呢,一般的广搜我们只需要考虑行动者的状态,一般就是那个能移动的个体,但是这道题,我们还需要将状态扩展为人和箱子的状态,箱子在不同的位置时,人即使坐标一样,也代表不同的状态。我们依然是以人为行动者,向四个方向进行搜索,当人的位置与当前箱子的位置重合,说明人推到了箱子,此时按照人移动的方向对箱子进行同样的移动,同时对状态数组进行增加来表示推过了一次箱子。此外,与一般的广度优先搜索不同,我们不能使用flag来标记哪些地方走过了哪些没走过,因为推动箱子之后可能存在更换方向推动箱子的情况,根据题解的方法,当我们推动箱子时,状态的改变会存入另一个队列,在下一轮循环中再进行广搜。

int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
int step[25][25][25][25];

int height, length;

int minPushBox(vector<vector<char> >& grid) {
	int sx, sy, ex, ey, bx, by;

	height = grid.size();
	length = grid[0].size();
	for(int i=0; i<height; i++){
		for(int j=0; j<length; j++){
			for(int m=0; m<height; m++){
				for(int n=0; n<length; n++){
					step[i][j][m][n] = INT_MAX;
				}
			}
		}
	}
	for(int i=0; i<height; i++){
		for(int j=0; j<length; j++){
			char c = grid[i][j];
			
			if(c=='S'){
				sx = i;
				sy = j;
			}
			if(c=='T'){
				ex = i;
				ey = j;
			}
			if(c=='B'){
				bx = i;
				by = j;
			}
		}
	}
	step[sx][sy][bx][by] = 0;
	queue<pair<pair<int, int>, pair<int, int> > > q;
	q.push(make_pair(make_pair(sx, sy), make_pair(bx, by)));
	while(!q.empty()){
		queue<pair<pair<int, int>, pair<int, int> > > q1;
		while(!q.empty()){
			pair<pair<int, int>, pair<int, int> > temp = q.front();
			q.pop();
			
			int npx = temp.first.first;
			int npy = temp.first.second;
			int nbx = temp.second.first;
			int	nby = temp.second.second;
			if(nbx==ex && nby==ey)
				return step[npx][npy][nbx][nby];
			
			for(int i=0; i<4; i++){
				int tpx = npx + dir[i][0];
				int tpy = npy + dir[i][1];
				 
				if(tpx<0||tpy<0||tpx>=height||tpy>=length||grid[tpx][tpy]=='#')
					continue;
				if(tpx==nbx && tpy==nby){
					// 推到了箱子 
					int tbx = nbx + dir[i][0];
					int tby = nby + dir[i][1];
					if(tbx<0||tby<0||tbx>=height||tby>=length||grid[tbx][tby]=='#')
						continue;
						
					if(step[tpx][tpy][tbx][tpy] <= step[npx][npy][nbx][nby] + 1)
						continue;
					
					step[tpx][tpy][tbx][tby] = step[npx][npy][nbx][nby] + 1;
					
					q1.push(make_pair(make_pair(tpx, tpy), make_pair(tbx, tby)));
				}else{
					// 没有推到箱子 
					if(step[tpx][tpy][nbx][nby] <= step[npx][npy][nbx][nby])
						continue;
					step[tpx][tpy][nbx][nby] = step[npx][npy][nbx][nby];
					q.push(make_pair(make_pair(tpx, tpy), make_pair(nbx, nby)));
				}	
			}
		}
		q.swap(q1);
	}
	return -1;
}

相比于题解的代码,我稍微改了一下数据结构的写法,按道理理解起来要稍微容易那么一点点,但是内存开销增加了不少,题解用的是一个数来表示二维的坐标,在定位以及四方向移动时会稍微麻烦点,所以我直接换成了四维数组,前两维表示人的位置,后两维表示箱子的位置,思路还是按照题解的思路。从人的位置开始四方向搜索,位置合法的情况下,判断是否与箱子的坐标产生了重叠,如果没有,那就继续搜索,但是在继续搜索的时候,为了避免重复走的问题,我们需要用step进行去重,也就是step[tpx][tpy][nbx][nby] <= step[npx][npy][nbx][nby]这个条件,这个条件实际上是在说,我们按照某个路径一直搜,搜索到头发现是个死路,如果不加这个条件,我们就会按照原路返回从而死循环,由于一开始我们设定了step的值全是最大值,只把起始位置改成了0,也就是说在第一轮搜索的过程中,所有走过的位置都改成了0,此时当走到死路的时候,就可以利用这个条件避免重走回头路。但是如果这个过程我们推到了箱子,事情就是另一回事了,我们推到了箱子,再走回头路就是允许的,因为此时我们可能是在利用回头路移动到箱子的另一个方向,但是回头路的回头路依然是不允许的,回头路的回头路依然是用step[tpx][tpy][nbx][nby] <= step[npx][npy][nbx][nby]进行筛选。值得一提的是,我们推到箱子之后,用q1进行了单独存储,这本质上是想让推到箱子作为开启下一次循环。我们每次循环,是从上一次的状态,在不走重复路的基础上,遍历所有路径找到能够推到箱子的新状态,下次在这部分的状态上进行继续的搜索。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

创新指南|5大策略让创新业务扩张最大避免“增长痛苦”

公司在开发和孵化新业务计划方面进行了大量投资&#xff0c;但很少有公司遵循严格的途径来扩大新业务规模。虽然80%的公司声称构思和孵化新企业&#xff0c;但只有16%的公司成功扩大了规模。典型案例是百思买在许多失败倒闭的扩大新业务取得了成功。它经历了建立新业务所需的3个…

手残也不该敲的命令

Linux命令是一种很有趣且有用的东西&#xff0c;但在你不知道会带来什么后果的时候&#xff0c;它又会显得非常危险。所以&#xff0c;在输入某些命令前&#xff0c;请多多检查再敲回车。 rm –rf rm –rf是删除文件夹和里面附带内容的一种最快捷的方法&#xff0c;但是细微的…

深度学习03-卷积神经网络(CNN)

简介 CNN&#xff0c;即卷积神经网络&#xff08;Convolutional Neural Network&#xff09;&#xff0c;是一种常用于图像和视频处理的深度学习模型。与传统神经网络相比&#xff0c;CNN 有着更好的处理图像和序列数据的能力&#xff0c;因为它能够自动学习图像中的特征&…

安全防线再升级 | 中睿天下全流量安全分析系统重磅回归

随着信息化的加速&#xff0c;企业网络日趋完善。企业数字化的加速&#xff0c;让越来越多的关键业务运行在计算机网络基础之上&#xff0c;越来越多的重要信息通过网络传送&#xff0c;企业网络面临日益严重的安全威胁&#xff0c;这些安全威胁以窃取信息和收集情报为主&#…

中文润色神器-中文润色软件

中文写作润色软件 中文写作润色软件是一种基于自然语言处理技术和人工智能算法的工具&#xff0c;旨在提高中文文本的语言风格、表达能力和可读性。它可以自动检测文本中出现的语法、拼写、标点符号等语言问题&#xff0c;并给出相应的修正和修改建议。 中文写作润色软件的主…

Spark 从入门到精通

Spark 从入门到精通 环境搭建 准备工作 创建安装目录 mkdir /opt/soft cd /opt/soft下载scala wget https://downloads.lightbend.com/scala/2.13.10/scala-2.13.10.tgz -P /opt/soft解压scala tar -zxvf scala-2.13.10.tgz修改scala目录名称 mv scala-2.13.10 scala-2下…

进程(二)

进程二 2.6 调度的概念、层次2.6.1 基本概念2.6.2 三个层次2.6.3 三层调度的联系、对比2.6.4 补充知识2.6.5 本小节总结 2.7 进程调度的时机、切换与过程、方式2.7.1 进程调度的时机2.7.2 切换与过程2.7.3 进程调度的方式2.7.4 总结 2.8 调度器/调度程序/闲逛线程2.9 调度算法的…

Python基础入门编程代码练习(六)

一、模拟房产经纪人来管理房屋信息 编写业务实现 家具类&#xff1a;HouseItem 属性&#xff1a;名字 name&#xff0c;占地面积 area 方法&#xff1a;__init__ , __str__ 类名&#xff1a;房子类 House 属性&#xff1a;户型 name&#xff0c;总面积&#xff1a;total_are…

Word怎么分页,提高效率就靠这3种方法!

案例&#xff1a;Word怎么分页 【文档要进行分页处理&#xff0c;但是我尝试了好多次还是不行&#xff01;大家知道Word怎么分页吗&#xff1f;】 在使用Microsoft Word处理文档时&#xff0c;我们常常需要进行分页操作。Word的分页功能可以将文档分成多个页面&#xff0c;以…

【Selenium上】——全栈开发——如桃花来

目录索引 Selenium是什么&#xff1a;下载和配置环境变量&#xff1a;1. 基本使用&#xff1a;导入五个常用包&#xff1a;基本代码&#xff1a; 实例引入&#xff1a;声明不同浏览器对象&#xff1a;访问页面&#xff1a; Selenium是什么&#xff1a; Selenium是一个用于Web应…

怎么把pdf中的某一页分出来?

怎么把pdf中的某一页分出来&#xff1f;PDF格式的文档在日常生活中是非常常见的&#xff0c;相信大家都对其有所了解&#xff0c;并且经常使用。它的主要特点是不允许用户随意编辑其中的内容&#xff0c;当我们仅需要阅读时&#xff0c;PDF文档无疑是十分方便的&#xff0c;尤其…

Python爬虫解读

爬虫&#xff1a; Python爬虫是指利用计算机程序或者脚本自动抓取网站数据的一种行为&#xff0c;通常是为了提取网站数据或者进行数据分析等目的。 Python 爬虫可以分为手动爬虫和自动爬虫两种。手动爬虫是指完全由人工编写代码来实现的爬虫&#xff0c;这种方式需要编写大量的…

NAS私有云存储 - 搭建Nextcloud私有云盘并公网远程访问

文章目录 摘要视频教程1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 转载自内网穿透工具的文章&#xff1a;使用Nextcl…

Selenium自动化测试中的PageObject模式

PageObject模式简介 众所周知&#xff0c;UI页面元素常常是不稳定的&#xff0c;在使用Selenium编写WebUI自动化测试用例时&#xff0c;随着测试脚本的增加&#xff0c;维护和更新这些元素便成为一个令人头疼的问题。 在普通模式下&#xff0c;脚本直接定位并操作元素&#xf…

进程(一)

进程&#xff08;一&#xff09; 2.1 进程的定义、组成、组织方式、特征2.1.1 定义2.1.2 组成2.1.3 组织方式2.1.4 特征2.1.5 本小节总结 2.2 进程的状态与转换2.2.1 进程的状态2.2.3 进程状态的转换2.2.4 本小节总结 2.3 进程控制2.3.1 基本概念2.3.2 进程控制相关的原语2.3.3…

【Java零基础入门篇】第 ⑤ 期 - 抽象类和接口(二)

博主&#xff1a;命运之光 专栏&#xff1a;Java零基础入门 学习目标 1.了解什么是抽象类&#xff0c;什么是接口&#xff1b; 2.掌握抽象类和接口的定义方法&#xff1b; 3.理解接口和抽象类的使用场景&#xff1b; 4.掌握多态的含义和用法&#xff1b; 5.掌握内部类的定义方法…

vue学习

{{}} 插值语法&#xff0c;应用vue实例容器中&#xff0c;获取标签值 v-bind v-bind&#xff1a;单向绑定&#xff0c;实例对象key的对应的值&#xff0c;绑定到vue实例容器标签的属性中 简写&#xff1a;: v-model v-model:双向绑定 注意&#xff1a;v-model只能应用于‘表单…

MathType7简体中文版数学公式编辑器下载安装教程

MathType一款专业的数学公式编辑器&#xff0c;理科生专用的必备工具&#xff0c;可应用于教育教学、科研机构、工程学、论文写作、期刊排版、编辑理科试卷等领域。2018年2月&#xff0c;MathType 7简体中文版正式发布&#xff0c;给用户带来全新的体验。MathType 是Windows和M…

jenkins,gitlab,实时构建推送

首先jdk&#xff0c;jenkins安装好&#xff0c;新版jenkins不支持jdk8 然后安装环境maven&#xff0c;git 环境配置 插件安装 gitlab插件 Build Authorization Token Root插件 插件环境整好之后新建个任务 源码管理&#xff0c;填入仓库https地址&#xff0c;添加git…

shell函数

目录 一、shell函数 1.函数的作用 2.函数的优点 二、shell函数的格式 2.1函数返回值return 2.2函数变量 三、函数传参 四、递归函数 4.1查找输入目录下文件及其子目录下文件 4.2将IP地址转换为二进制 五、函数数据库 一、shell函数 1.函数的作用 把程序里需要多次…