推箱子

Posted 929code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了推箱子相关的知识,希望对你有一定的参考价值。

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

一. 01广度优先搜索 + 双端队列

将人与箱子位置状态看做一个节点,在该题中人移动无需代价,即节点转移无需代价,所以边的权值为0
推动箱子移动耗费代价,推动箱子的边权值为1
最终目标是箱子达到目标位置,人的位置可能有多个,问题转化为求取对应状态图,到任意满足条件节点的最小代价
将坐标转化为一维,得到状态dp[i][j],i表示人的位置,j表示箱子位置,用来防止重复遍历,以及进行剪枝
接下来进行状态转移

class Solution 
public:
    int minPushBox(vector<vector<char>>& grid) 
        int m = grid.size(), n = grid[0].size();
        int sx, sy, bx, by; // 玩家、箱子的初始位置
        for (int x = 0; x < m; x++) //读取初始二维位置
            for (int y = 0; y < n; y++) 
                if (grid[x][y] == \'S\') 
                    sx = x;
                    sy = y;
                 else if (grid[x][y] == \'B\') 
                    bx = x;
                    by = y;
                
            
        

        auto ok = [&](int x, int y) -> bool  // 判断当前位置是否合法
            return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != \'#\';
        ;

        vector<int> d = 0, -1, 0, 1, 0;//对应四个移动方向

        vector<vector<int>> dp(m * n, vector<int>(m * n, INT_MAX));//初始化二维dp数组
        deque<pair<int, int>> q;//状态队列
        dp[sx * n + sy][bx * n + by] = 0; // 初始位置的dp初始化,未进行任何移动
        q.push_back(sx * n + sy, bx * n + by);//初始状态入队
        while (!q.empty()) //广度优先搜索
                auto [s1, b1] = q.front();
                q.pop_front();
                int sx1 = s1 / n, sy1 = s1 % n, bx1 = b1 / n, by1 = b1 % n;//一位坐标转二维
                if (grid[bx1][by1] == \'T\')  // 箱子已被推到目标处,由于是广度优先,必然是最小代价
                    return dp[s1][b1];
                for (int i = 0; i < 4; i++)  // 玩家向四个方向移动到另一个状态
                    int sx2 = sx1 + d[i], sy2 = sy1 + d[i + 1], s2 = sx2*n+sy2;//人的下一个移动位置
                    if (!ok(sx2, sy2)) continue; // 玩家位置不合法,不能进行该次移动
                    if (bx1 == sx2 && by1 == sy2)  // 如果人位置与箱子位置重叠,说明推动箱子
                        int bx2 = bx1 + d[i], by2 = by1 + d[i + 1], b2 = bx2*n+by2;//箱子移动方向与人一致
                        if (!ok(bx2, by2) || dp[s2][b2] <= dp[s1][b1] + 1)  // 箱子位置不合法或状态已访问,同时剪枝避免更糟糕的状态
                            continue;//跳过,不能往该方向移动

                        dp[s2][b2] = dp[s1][b1] + 1;//状态转移
                        q.push_back(s2, b2);//下一个状态入队
                     else 
                        if (dp[s2][b1] <= dp[s1][b1])     continue;//状态已访问跳过,也是进行剪枝
                         
                        dp[s2][b1] = dp[s1][b1];//新的状态
                        q.push_front(s2, b1);//新的状态入队,这里是01广度优先精髓,入的是当前层次的队,说明与前面的节点属于同一广度(因为边权值为0),可以使得优先搜索代价小的
                    
                
        
        return -1;
    
;

推箱子游戏

推箱子游戏

推箱子的游戏大家肯定玩过,尤其是在按键手机的时代,这种游戏是手机必装的自带游戏,那么你有没有考虑过它是怎么生成的呢?

今天我们用java开发一下这个游戏

背景

首先是游戏画面的设置,背景设置就是继承JFrame类,设置窗口,和其他游戏一样:

setSize(720,720);
setVisible(true);
setResizable(false);
setLocation(300,20);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

地图

其次,我们要画出游戏的地图,地图是使用一个二维数组表示的,像这样:

00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000011100000000
00000000014100000000
00000011113100000000
00000014254111000000
00000011132341000000
00000000121111000000
00000000141000000000
00000000111000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

不同的数字分别表示不同的元素,其中5是我们推箱子的主人公,我们加载完这个数组,for循环遍历,根据不同的值解析成不同的图片,然后通过主类进行绘制:

for(int i=0; i<20; i++)
      for(int j=0; j<20; j++)
        
          g.drawImage(myImage[map[j][i]],i*len,j*len,this);
        

人物移动

对于人物的移动,人物是通过我们键盘的上下左右键来控制的,我们需要实现键盘的监听接口:

public void keyPressed(KeyEvent e)
  
    if(e.getKeyCode()==KeyEvent.VK_UP)moveup();
    if(e.getKeyCode()==KeyEvent.VK_DOWN)movedown();
    if(e.getKeyCode()==KeyEvent.VK_LEFT)moveleft();
    if(e.getKeyCode()==KeyEvent.VK_RIGHT)moveright();
    
  

由于是个二维平面的俯视图,因此人物有上下左右的人物都是不同的图片,我们根据上下左右键来切换显示人物不同的图片,实现方法都在各自的方法中实现了

过关

那么怎么判断这个游戏什么过关了呢?

数组中的4的位置表示箱子应该放的位置,当箱子推到4的位置的时候,我们用个计数器记录,当所有箱子都推进去的时候,也就成功了。

boolean iswin()
  
    boolean num=false;
    out:for(int i=0; i<20; i++)
      for(int j=0; j<20; j++)
    
      if(maptmp[i][j]==4||maptmp[i][j]==9)
        if(map[i][j]==9)num=true;
          else num=false;break out;
    
    return num;
  

这就是推箱子的大体逻辑了

总结

这篇文章主要讲了用java来实现推箱子的功能,其实推箱子的操作就是对二维数字的操作,通过开发游戏我们也能学到很多知识,这里不只是涉及二维数组的定义和操作,更有键盘的监听事件的处理,因为我们游戏是通过键盘来控制人物的。

以上是关于推箱子的主要内容,如果未能解决你的问题,请参考以下文章

推箱子

推箱子

网易笔试题:推箱子

C语言零基础开发推箱子小游戏

推箱子

推箱子