【LeetCode困难】1263. 推箱子

news2025/2/21 20:59:48

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

游戏地图用大小为 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进行了单独存储,这本质上是想让推到箱子作为开启下一次循环。我们每次循环,是从上一次的状态,在不走重复路的基础上,遍历所有路径找到能够推到箱子的新状态,下次在这部分的状态上进行继续的搜索。

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

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

相关文章

设计模式|代理模式

代理模式介绍 ​代理模式指为其他对象提供一种代理&#xff0c;以控制对这个对象的访问。在某些情况下&#xff0c;一个对象若不能直接引用另一个对象&#xff0c;而代理对象可以在客户端与目标对象之间起到中介的作用。 代理模式使用场景 普通代理 普通代理模式是指在代理模…

C++的string类使用介绍

string类 1.为什么要学习string类&#xff1f;1.1.C语言中的字符串1.2. 日常中 2. 标准库中的string类2.2 string类(对于单字节的字符)的常用接口说明①string常见的构造函数②string类对象的容量操作③string类对象的访问以及遍历操作④string类对象的修改操作⑤string类非成员…

OrCAD怎样把原理图输出为DXF格式

OrCAD怎样把原理图输出为DXF格式 又有段时间没分享文章了&#xff0c;想想主要还是自媒体写点内容确实不容易&#xff0c;要不断坚持下来更不容易&#xff0c;一直以来也就是凭着“乐于分享”的心在不定时更新。 今天分享的主题是&#xff1a;OrCAD怎样把原理图输出为DXF格式…

标准CSO

Cheng R, Jin Y. A competitive swarm optimizer for large scale optimization[J]. IEEE transactions on cybernetics, 2014, 45(2): 191-204. 1.1 背景介绍 CSO&#xff08;competitive swarm optimizer&#xff09;算法是在PSO&#xff08;particle swarm optimization&a…

医日健“数智药房”解锁购药新模式

“现在买药这么方便&#xff0c;半夜拉肚子过来自助付款、自助取药&#xff0c;还能连线医生&#xff0c;很快就买好了药。”上海市宝山区消费者王先生惊喜地说。近日&#xff0c;宝山区一国大药房医日健 “智慧药房”正式上线营业&#xff0c;该药房实现自助式、无接触就医购药…

如何添加团队成员到你的项目

本文介绍在YonBuilder移动开发中&#xff0c;如何把你的团队成员添加到你的应用中&#xff0c;让团队成员也具备应用的相关配置&#xff0c;代码拉取&#xff0c;打包编译等功能权限。 简单来说把「团队成员添加到你的项目」&#xff0c;一共需要三步大流程操作&#xff0c;具…

基于AT89C51单片机的篮球比赛计时计分器

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87778138?spm1001.2014.3001.5503 源码获取 主要内容&#xff1a; 比赛的计分和计时的工具大多是很简陋的比分牌&#xff0c;十分的不方便。而且大多由于缺少24秒…

Meta-learning综述

文章目录 几个概念1&#xff09;监督、无监督、弱监督学习&#xff08;Weakly Supervised Learning&#xff09;以及 自监督学习&#xff08;Self-supervised Learning&#xff09;2&#xff09;域偏移、域适应、域泛化3&#xff09;N-way K-shot&#xff08;Few-shot learning…

【Java虚拟机】JVM诊断神器Arthas入门实操

1.Arthas简介快速入门 阿里开源的Java诊断工具&#xff0c;它可以在运行时对Java应用程序进行动态诊断和调试 当你遇到以下类似问题而束手无策时&#xff0c;Arthas可以帮助你解决 这个类从哪个 jar 包加载的&#xff1f;为什么会报各种类相关的 Exception&#xff1f;我改的代…

国考省考行测:数字推理题2

国考省考行测&#xff1a;数字推理题2 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 …

改进YOLOv8 | 特征融合篇 | YOLOv8 应用 BiFPN 结构 | 《 EfficientDet: 可扩展和高效的目标检测》

模型效率在计算机视觉中变得越来越重要。在本文中,我们系统地研究了目标检测中的神经网络架构设计选择,并提出了几种关键的优化方法来提高效率。首先,我们提出了一种加权双向特征金字塔网络(BiFPN),它可以实现简单快速的多尺度特征融合;其次,我们提出了一种复合缩放方法…

从期望最大化(EM)到变分自编码器(VAE)

本文主要记录了自己对变分自编码器论文的理解。 Kingma D P, Welling M. Auto-encoding variational bayes[J]. arXiv preprint arXiv:1312.6114, 2013. https://arxiv.org/abs/1312.6114 1 带有潜在变量的极大似然估计 假设我们有一个有限整数随机数发生器 z ∼ p θ ( z ) …

Linux | 学习笔记(适合小白)上

操作系统概述&#xff1a; 计算机是由硬件和软件这两个主要部分组成的操作系统是软件的一类&#xff0c;主要作用是协助用户调度硬件工作&#xff0c;充当用户和计算机硬件之间的桥梁常见的操作系统&#xff1a;PC端&#xff1a;Windows&#xff0c;Linux&#xff0c;MacOS&…

电子邮件市场中,如何使用您的Gmail?

Gmail凭借其直观的界面、慷慨的免费存储空间&#xff08;与其他Google工具共享15 GB&#xff0c;如Google Drive和Photos&#xff09;以及作为常规Gmail账户附加的各种免费生产力工具&#xff0c;在电子邮件市场占据主导地位。但是&#xff0c;人们对Google如何使用您的电子邮件…

决策引擎平台建设方案

文档修订历史 时间版本主要内容2023.05.12v1.0.0初始化 1. 概述 1.1 需求 1.1.1 需求背景 当同一个业务场景中&#xff0c;有非常多的业务分支后&#xff0c;需要有非常多的 if 判断&#xff0c;来承载这些简单的业务逻辑&#xff0c;但随着业务的发展&#xff0c;业务逐渐…

Java --- redis7之大数据统计之Bitmap

目录 一、大数据统计之Bitmap 1.1、面试题 1.2、京东签到领取京豆 一、大数据统计之Bitmap 1.1、面试题 1、日活统计 2、签到打卡 3、最近一周的活跃用户等 1.2、京东签到领取京豆 一个月的签到天数&#xff0c;连续签到数 方案1&#xff1a;使用MySQL来实现(小项目) …

NTLM 中继攻击的几种非主流玩法

在企业组织中的常见的一种安全风险是凭证重用&#xff0c;当攻击者攻击 NT LAN Manager 身份验证协议(以下简称 NTLM 身份验证)时就会出现这样的风险&#xff0c;而这个协议通常会在 微软的 活动目录 中默认启用。 NTLM 认证中的不安全性已经被安全研究人员发现超过15年了。该…

使用Vue CLI脚手架

章节概述&#xff1a; 使用Vue CLI脚手架初始化脚手架分析脚手架结构render函数修改默认配置ref属性props配置项mixin混入plugin插件scoped样式Todo-List案例WebStorage自定义事件绑定解绑全局事件总线消息的订阅与发布$nextTick过渡与动画 提示&#xff08;我没有使用markdo…

c++ 常用总结(二)

1. ① 可变参数... 、__VA_ARGS__与##__VA_ARGS__ 结论&#xff1a;##__VA_ARGS__中##的作用就是去掉前面多余的,号 &#xff0c;在使用自定义打印的时候&#xff0c;推荐##__VA_ARGS__而不是__VA_ARGS__ C语言##__VA_ARGS__的用法_fengwang0301的博客-CSDN博客 例1 __VA…

良心推荐!数学建模基础知识-MATLAB快速上手,最适合新手学习的Matlab快速入门教程

目录 1. 如何打开matlab的文件 第一种方法&#xff1a; 第二种创建脚本文件的方法。 2. 如何运行一段代码 写在命令行 写在脚本 3.some tips about matlab 工作区储存的数据 如何加入断点&如何终止运行&代码分节 1. 如何打开matlab的文件 我们写的源代码可以…