推箱子(广度优先BFS+动态规划DP)

该文章介绍了一道力扣(LeetCode)题目,涉及使用Java编程解决基于广度优先搜索(BFS)和动态规划的问题。解题思路重点在于理解如何通过人推动箱子,计算最小移动次数。文章通过初始化状态,利用队列进行广度优先遍历,并用二维数组记录不同位置的最小移动次数。
摘要由CSDN通过智能技术生成

 力扣原题,java代码

解题思路:

本题考查的知识点是广度优先和动态规划,以箱子当作主体,人可以从上下左右四个方向推箱子,故而对于箱子的每个位置,我们需要考虑人从不同的方向推箱子产生的代价(即箱子从初始位置到当前位置的最小移动次数)。

故而int[][] dp = new int[n*m][4];

dp[bnew][k]:表示人从方向k将箱子移动到新位置bnew的最小移动次数

备注:人从初始位置推箱子,箱子移动后的最小移动次数一定是1.

初始化:将箱子的初始位置,人的初始位置,初始方向(因为初始情况不存在箱子的移动,所以没有初始方向,设置为-1,与后面的情况要分开讨论)添加入队列中。(备注:队列先进先出适合广度优先,堆栈先进后出符合深度优先)

1.人从箱子的-k侧将箱子推移到箱子的k侧(备注:若k侧表示左侧,则-k侧就表示右侧)

①判断箱子移动后的位置和人推箱子的位置是否越界;

②若当前是初始位置,则将箱子移动后dp[bnew][k]=1;
   若不是初始位置,则判断 人从方向k将箱子移动到位置bnew 这种情况是否已经探索过,若探索过,则不再探索,若没有探索,则探索该种情况;

③判断人是否能从当前位置到达箱子的-k侧(判断箱子的-k侧与人的位置是否是联通的,注意:箱子是不能越过的)

④若箱子移动后的位置与目标位置重合,则返回 dp[bnew][k];

    反之,则将箱子移动后的位置和人当前的位置以及移动方向存入队列中,继续当前循环。

import java.util.*;

class Solution {
        int[] d = new int[]{0,1,0,-1,0};
    public int minPushBox(char[][] grid) {
        int n = grid.length, m = grid[0].length;
        int s=-1, b=-1; // 广度优先
        int[][] dp = new int[n*m][4]; // 箱子和人的位置
        for(int i=0; i<n; i++) {
            for (int j = 0; j<m; j++) {
                Arrays.fill(dp[i*m+j],400);
                if(grid[i][j] == 'S') {
                    s = i*m+j;
                } else if(grid[i][j] == 'B') {
                    b = i*m+j;
                }
            }
        }
        //Arrays.fill(dp[b],0);
        Queue<int[]> q = new LinkedList<>(); // 箱子的位置
        q.add(new int[]{b,s,-1});
        while (!q.isEmpty()) {
            int[] bs = q.poll();
            int bi = bs[0]/m, bj=bs[0]%m;
            for (int k = 0; k < 4; k++) {
                // 人从箱子的-k侧将箱子推移到箱子的k侧
                int i = bi+d[k], j=bj+d[k+1]; // 箱子的新位置
                int u = bi-d[k], v=bj-d[k+1]; // 人推箱子的位置
                int kp = bs[2]; // 箱子和人的位置,人从哪个方向推箱子
                int bnew = i*m+j;
                if(isOk(grid,i,j,n,m) && isOk(grid,u,v,n,m) && 
                (kp == -1 || dp[bnew][k] > dp[bs[0]][kp]+1)) {
                    // 人是否能从当前位置达到箱子的-k侧
                    boolean[] vis = new boolean[n*m]; vis[bs[0]] = true; // 箱子的位置不能被访问
                    if(isFeaseable(grid,bs[1],u*m+v,vis,n,m)) {
                        // 当前箱子可以被人由-k推到k
                        dp[bnew][k] = kp==-1?1:dp[bs[0]][kp]+1; // 箱子从原本的位置移动1步
                        if(grid[i][j] == 'T') {
                            return dp[bnew][k];
                        }
                        q.add(new int[]{bnew,bs[0],k});
                    }
                }
            }
        }
        return -1;
    }

    // 人当前的位置与箱子k侧的位置联通
    public boolean isFeaseable(char[][] grid, int s, int b, boolean[] vis, int n, int m) {
        if(s == b) {
            return true;
        }
        Queue<Integer> q = new LinkedList<>();
        q.add(b);
        while (!q.isEmpty()) {
            int bij = q.poll();
            int bi=bij/m, bj=bij%m;
            for (int k = 0; k < 4; k++) {
                int i=bi+d[k], j=bj+d[k+1];
                int b1 = i*m+j;
                if(isOk(grid,i,j,n,m) && !vis[b1]) {
                    if(s == b1) {
                        return true;
                    }
                    vis[b1] = true;
                    q.add(b1);
                }
            }
        }
        return false;
    }

    public boolean isOk(char[][] grid, int i, int j, int n, int m) {
        if(i>=0 && i<n && j>=0 && j<m && grid[i][j] != '#') {
            return true;
        }
        return false;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
´问题描述: 码头仓库是划分为n×m个格子的矩形阵列。有公共边的格子是相邻格子。当前仓库中 有的格子是空闲的;有的格子则已经堆放了沉重的货物。由于堆放的货物很重,单凭仓库管 理员的力量是无法移动的。仓库管理员有一项任务,要将一个小箱子推到指定的格子上去。 管理员可以在仓库中移动,但不能跨过已经堆放了货物的格子。管理员站在与箱子相对的空 闲格子上时,可以做一次推动,把箱子推到另一相邻的空闲格子。推箱时只能向管理员的对 面方向推。由于要推动的箱子很重,仓库管理员想尽量减少推箱子的次数。 ´编程任务: 对于给定的仓库布局,以及仓库管理员在仓库中的位置和箱子的开始位置和目标位置, 设计一个解推箱子问题的分支限界法, 计算出仓库管理员将箱子从开始位置推到目标位置所 需的最少推动次数。 ´数据输入: 由文件input.txt提供输入数据。输入文件第 1 行有 2个正整数 n和 m(1<=n,m<=100) , 表示仓库是n×m个格子的矩形阵列。接下来有 n行,每行有 m个字符,表示格子的状态。 S 表示格子上放了不可移动的沉重货物; w 表示格子空闲; M 表示仓库管理员的初始位置; P 表示箱子的初始位置; K 表示箱子的目标位置。 ´结果输出: 将计算出的最少推动次数输出到文件 output.txt。如果仓库管理员无法将箱子从开始位 置推到目标位置则输出“No solution!” 。 输入文件示例 输出文件示例 input.txt output.txt
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值