推箱子游戏是将箱子推到目标点,是一个经典的游戏,这个问题存在一个最优解,即最短走法,这里通过分析一个箱子与多个箱子,探讨问题的解法。

一个箱子问题链接:leetcode推箱子
这个问题是一个阉割版,不考虑人走的步数,只考虑箱子被推到目标位置(前提是人不被卡住)
思路
考虑使用BFS,计算每个点离目标点的可推最短距离(自定义距离,可以推动的最短距离)
为了简化计算,还要用一个数组记录已经遍历过的点。 计算箱子所在当前点与目标点的距离,从初始点向四个方向扩展,直到找到目标点(反过来计算也可以),为了避免找到的不是最优解,可以在每个点加入队列时按照移动步数steps进行排序。
代码

class Solution {public int minPushBox(char[][] grid) {int rlen = grid.length;int llen = grid[0].length;//visited[i][j][m][n] = true 表示 人物在 (i, j) 坐标和 箱子在 (m, n) 坐标 这个状态已经访问过了boolean[][][][] visited = new boolean[rlen][llen][rlen][llen];int sx = -1, sy = -1, tx = -1, ty = -1, bx = -1, by = -1;int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};for (int i = 0; i < rlen; i++) {for (int j = 0; j < llen; j++) {if (grid[i][j] == 'S') {sx = i;sy = j;}if (grid[i][j] == 'T') {tx = i;ty = j;}if (grid[i][j] == 'B') {bx = i;by = j;}}}/*当人物在箱子的左边时,人物可以选择向右边走当人物在箱子的右边时,人物可以选择向左边走当人物在箱子的上边时,人物可以选择向下边走当人物在箱子的下边时,人物可以选择向上边走这样才能保证步数最少,否则,如果箱子在左边,人物还向着右边走,那么就距离箱子越来越远,这是毫无意义的步数无法满足条件的情况:如果箱子会自己走的话,那么简单的 bfs 就能够完成了,但是这里需要人物来推动箱子这意味着箱子可能虽然旁边就是终点,但是可能不存在能够容纳人物的地方来推动箱子比如 下图,虽然 箱子 B 旁边就是终点 T,如果它能够自己走的话直接一步到终点但是由于需要推动,而却不存在容纳人物 S 的位置来 箱子 B 到达终点 T# # # ## T B ## . S #什么时候箱子的位置会发生改变?当人物向上下两个方向走的时候,如果人物的下一个位置就是箱子的位置,那么相当于顶着箱子前进,那么箱子同时也要往前进因为人物的移动不算在步数里的,因此可能移动的时候当前箱子推的步数很大,比如示例 1 ,人物绕了一大圈如果最优的情况,即最少的推箱子步数就是这绕一大圈的,但是别的状态还在推箱子,它能够更快的到达终点,但是推箱子步数很大所以最先碰到终点的不一定是步数最少的,所以需要使用一个优先队列*/PriorityQueue<Node> queue = new PriorityQueue<>((a, b) -> a.steps - b.steps);  // 按steps进行升序排列,确保找到的一定是最短的queue.add(new Node(0, sx, sy, bx, by));    // 初始点while (!queue.isEmpty()) {Node t = queue.poll();int currentBoxX = t.bx, currentBoxY = t.by;if (currentBoxX == tx && currentBoxY == ty) {     // 箱子到了目标点return t.steps;}visited[t.hx][t.hy][currentBoxX][currentBoxY] = true;//往四个方向走for (int[] p : directions) {int newPx = t.hx + p[0];int newPy = t.hy + p[1];int newBx = t.bx;int newBy = t.by;int newStep = t.steps;//人物的前进位置刚好是箱子的位置,那么箱子的位置也要发生改变if (newPx == t.bx && newPy == t.by) {newBx += p[0];newBy += p[1];//箱子动了,步数 +1newStep++;}//越界或者在障碍物上,那么跳过if (!legalIndex(newBx, rlen, newBy, llen) || !legalIndex(newPx, rlen, newPy, llen)|| grid[newPx][newPy] == '#' || grid[newBx][newBy] == '#') {continue;}if (!visited[newPx][newPy][newBx][newBy]) {queue.add(new Node(newStep, newPx, newPy, newBx, newBy));}}}return -1;}private static boolean legalIndex(int x, int rlen, int y, int llen) {return x >= 0 && x < rlen && y >= 0 && y < llen;}static class Node {int steps;int hx;int hy;int bx;int by;public Node(int steps, int hx, int hy, int bx, int by) {this.steps = steps;this.hx = hx;this.hy = hy;this.bx = bx;this.by = by;}}
}

然而,这个解法不够智能,只击败了10%的java提交,我们有以下方法可以进行优化

使用并查集进行优化

class Solution {//1. 并查集判断能否到达//2. BFS 箱子int[][] dir = new int[][]{{-1, 0}, {1, 0}, {0, 1}, {0, -1}};public int minPushBox(char[][] grid) {int m = grid.length, n = grid[0].length;int px = 0, py = 0, tx = 0, ty = 0, bx = 0, by = 0;for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {if(grid[i][j] == 'B') {bx = i;by = j;} else if(grid[i][j] == 'S') {px = i;py = j;} else if(grid[i][j] == 'T') {tx = i;ty = j;}}}int[] parent = buildSet(grid, m, n);int sp = find(parent, px * n + py);int st = find(parent, tx * n + ty);int sb = find(parent, bx * n + by);if(!(sp == st && st == sb)) return -1;Queue<int[]> que = new LinkedList<>();Set<String> visited = new HashSet<>();que.offer(new int[]{bx * n + by, px * n + py});visited.add((bx * n + by) + "," + (px * n + py));int res = 0;while(!que.isEmpty()) {for(int size = que.size(); size > 0; size--) {int[] p = que.poll();if(p[0] == tx * n + ty) return res;int r = p[0] / n, c = p[0] % n;char ch = grid[r][c];grid[r][c] = '#';parent = buildSet(grid, m, n);for(int[] d : dir) {int x = r + d[0], y = c + d[1];int rx = r - d[0], ry = c - d[1];if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != '#' && rx >= 0 && rx < m && ry >= 0 && ry < n && grid[rx][ry] != '#') {if(find(parent, rx * n + ry) == find(parent, p[1]) && visited.add((x * n + y) + "," + p[0])) {que.offer(new int[]{x * n + y, p[0]});}}}grid[r][c] = (char)(res + '0');}res++;}return -1;}private int[] buildSet(char[][] grid, int m, int n) {int[] parent = new int[m * n];for(int i = 0; i < m * n; i++) parent[i] = i;for(int i = 0; i < m; i ++) {for(int j = 0; j < n; j++) {if(grid[i][j] != '#') {if(i > 0 && grid[i-1][j] != '#') union(parent, (i-1)*n+j, i*n+j);if(i < m-1 && grid[i+1][j] != '#') union(parent, (i+1)*n+j, i*n+j);if(j > 0 && grid[i][j-1] != '#') union(parent, i*n+j-1, i*n+j);if(j < n-1 && grid[i][j+1] != '#') union(parent, i*n+j+1, i*n+j);}}}return parent;}private void union(int[] p, int a, int b) {int pa = find(p, a), pb = find(p, b);if(pa != pb) p[pa] = pb;}private int find(int[] p, int a) {return p[a] = p[a] == a ? a : find(p, p[a]);}
}

击败了41%的java提交

更好的解法

可以用启发式搜索对这个寻找过程进行加速。
箱子用了A*,估价函数f返回值里,第一比较值是取曼哈顿距离和已走步数之和,保证在箱体内搜索出步数最优值,后续比较值是box的坐标。
人用best-first算法,原理和A*相似,只是不用考虑最短步数,只优先考虑距离终点近点的扩展。
因为复数不能在堆里直接比较,所以加了time和pTime计数器保证优先处理先进堆的元素,且不会比较到复数坐标。

A*算法和BF算法单轮无障碍情况下不大于
O((M+N)log(M+N))的时间,对数计算来自于堆的复杂度,单轮有障碍则是
O(MNlog(MN)),所以总的最坏时间复杂度则是
O(M 2 N 2 log(MN) 2),考虑三个关键点的大多分布在地图周围,但人的行走步数拓展层次多数情况下不会很深,且由于估价函数的存在,大大提升了寻路效率,线上测试平均优化掉一半时间是符合逻辑的。

代码:

推箱子游戏解法的数学分析与代码实现(Leetcode1263等未完)相关推荐

  1. 推箱子游戏破解方案的MATLAB代码实现

    <推箱子>游戏破解方案的MATLAB代码实现 前言 文件介绍与使用说明 (1)主脚本Sokoban.m (2)函数can_box() (3)函数find_can_boy() (4)函数ha ...

  2. python推箱子代码详细讲解_Python使用tkinter模块实现推箱子游戏

    前段时间用C语言做了个字符版的推箱子,着实是比较简陋.正好最近用到了Python,然后想着用Python做一个图形界面的推箱子.这回可没有C那么简单,首先Python的图形界面我是没怎么用过,在网上找 ...

  3. 使用C++代码打造:史上最恶心的“推箱子”游戏,你敢再坑爹点吗?

    根据J2ME版推箱子游戏改编,现改版为VC++版,功能上一模一样,不过现在可以在Windows上运行了,而非限制于手机.游戏界面看上去还不错. 项目截图: 代码截图: 推箱子游戲是一個來自日本的古老游 ...

  4. python推箱子游戏代码_用python入门知识做推箱子游戏,若能打过第三关,则可以学会编程...

    不得不说,Python小游戏是最适合入门编程的项目,因为太简单! 无论懂或完全不懂python,这样的小游戏,你都可以直接开发出来,原因很简单: 在详细教程里,会有从零基础开始,一步一步的教你完成这个 ...

  5. c#推箱子小游戏代码_C# 推箱子游戏源码(带音效/关卡)

    C# 推箱子游戏源码(带音效/关卡) c# 2021-1-29 下载地址 https://www.codedown123.com/62444.html Vs2010 FrameWork 4.0 具有音 ...

  6. 推箱子游戏 —— 代码我来写

    问题描述: 一天嘻哈华的弟弟正在玩一款推箱子游戏,他就想我能不能自己写个推箱子游戏呢? 源代码: // 获取并打印地图 void getMap(){int i, j;for(i=0; i<INF ...

  7. EasyX实现推箱子游戏

    文章目录 1 项目需求 2 模块划分 3 项目实现 3.1 地图初始化 3.2 热键控制 3.3 推箱子控制 3.4 游戏结束 1 项目需求 实现一款推箱子游戏,效果如下图所示,具体规则: 箱子只能推 ...

  8. 项目: 推箱子游戏【c/c++】

    很早之前写的一个推箱子的游戏 目录 最终效果 代码 最终效果 代码 #include<stdio.h> #include<stdlib.h> #include<graph ...

  9. 推箱子java下载_Java实现简单推箱子游戏

    本文实例为大家分享了Java实现简单推箱子游戏的具体代码,供大家参考,具体内容如下 *编写一个简易的推箱子游戏,使用10*8的二维字符数据表示游戏画面,H表示墙壁; &表示玩家角色: o表示箱 ...

最新文章

  1. 天池大赛 + CV语义分割 + 78万奖金:全国数字生态创新大赛来了!
  2. 世界名画 | 陌上花开,可缓缓归矣
  3. for 循环里调用ajax,for循环中ajax异步问题如何解决?
  4. RabbitMq--4--集群(转载)
  5. 收货地址 - 需求分析与表设计
  6. 微软Build 2017第一天:值得开发者关注的热点
  7. c语言代码大全_从学生到专家,C语言开发必读的8本书
  8. 翻车事故频发,原来是开发者漏了这一步!
  9. 清除Eclipse中保存的github密码
  10. ftp文件传输有服务器吗,ftp文件传输有服务器吗
  11. 简单好用的洗鼻子方法?
  12. 大唐双龙传JAVA版小游戏_大唐双龙传_JAVA游戏免费版下载_7723手机游戏[www.7723.cn]...
  13. 多个路由器无线桥接,共享网络
  14. 2021 ICPC 银川打铜记
  15. 饥荒联机版专用服务器怎么修改小偷包,饥荒联机小偷背包代码 | 手游网游页游攻略大全...
  16. qt tableWidget 去掉网格线
  17. [MachineLearning] 机器学习速成笔记 - Bilibili
  18. 天翼云联想云坚果云我应该选择哪一个呢?
  19. Matlab------------怎么取一个复数的实部和虚部
  20. TransC:Differentiating Concepts and Instances for Knowledge Graph Embedding

热门文章

  1. java游戏笑傲_快乐家族-笑傲武林
  2. JQuery中toggle的用法
  3. 游戏编程这些年的苦与乐
  4. Beatifulsoup4 兄弟节点 next_sibling和next_sibling()
  5. JavaSE基础篇——超详细,Java入门,这一篇就够了
  6. Axure的布尔运算/发布与设置
  7. Python3 爬虫学习笔记 C08【解析库 Beautiful Soup】
  8. 中兴通讯全资子公司获准挂牌新三板
  9. 2022-2028全球与中国编码标记系统和解决方案市场现状及未来发展趋势
  10. Java_Hive自定义函数_UDF函数清洗数据_清洗出全国的省份数据