推箱子问题

一 问题描述

你站在一个由方格组成的二维度迷宫中,这些格子可能被填满岩石,也可能没被填满岩石。你可以一步一个格子地往北、往南、往东或往西移动。这样的动作叫作“走”。其中一个空单元格包含一个箱子,你可以站在箱子旁边,推动箱子到相邻的自由单元格。这样的动作叫作“推”。箱子除了用推的方式,不能移动,如果你把它推到角落里,就再也不能把它从角落里拿出来了。将其中一个空单元格标记为目标单元格。你的工作是通过一系列走和推把箱子带到目标格子里。由于箱子很重,所以要尽量减少推动的次数。编写程序,计算最后的移动顺序。

二 输入和输出

1 输入

数据包含多个测试用例。每个测试用例的第 1 行都包含两个整数 r 和 c,表示迷宫的行数和列数。接下来是 r 行,每行 c 个字符,每个字符都描述迷宫中的一个格子,对被填满岩石的格子用“#”表示,对空格用“.”表示,对起始位置用“S”表示,对箱子的起始位置用“B”表示,对目标单元格用“T”表示。输入端以两个 0 终止。

2 输出

对于输入中的每个迷宫,都首先输出迷宫的编号。如果无法将箱子带到目标单元格里,则输出“Impossible”,否则输出一个最小推动次数的序列。如果多个这样的序列,则请选择一个最小总移动(走和推)次数的序列。如果仍然有多个这样的序列,则任务一个都可被接受。将序列输出为由 N、S、E、W、n、s、e 和 w 组成的字符串,大写表示推,小写表示走,祖母分别表示北、南、东和西这 4 个方向。在每个测试用例之后都输出一个空行。

三 输入和输出样例

1 输入样例

1 7

SB....T

1 7

SB..#.T

7 11

###########

#T##......#

#.#.#..####

#....B....#

#.######..#

#.....S...#

###########

8 4

....

.##.

.#..

.#..

.#.B

.##S

....

###T

0 0

2 输出样例

Maze #1

EEEEE

Maze #2

Impossible.

Maze #3

eennwwWWWWeeeeeesswwwwwwwnNN

Maze #4

swwwnnnnnneeesssSSS

四 分析

本问题是推箱子问题,要求先保证推箱子的次数最少,在此基础上再让人走的步数最少。推箱子时,人只有站在箱子反方向的前一个位置,才可以将箱子推向下一个位置。因此在移动箱子时,不仅需要判断新位置有没有岩石,还需要判断人是否可以到达反方向的前一个位置,在两者均有效时,才会让人移动。

先求解箱子到目标位置的最短路径(BFS1),在推箱子的过程中,每推一步,都根据推的方向和箱子的位置得到箱子的前一个位置,再求解人到达这个位置的最短路径(BFS2)。在 BFS1 里面嵌套 BFS2 ,属于嵌套广度优先搜索。

五 算法设计

1 定义一个标识数组 vis[][],并将其初始化为 0,标识所有位置都未被访问。

2 创建一个队列 q 维护箱子的状态,将人的初始位置 (sx,sy)、箱子的初始位置(bx,by)和初始路径(“”)入队,标记箱子的位置 vis[bx][by]=1。

3 如果队列不空,则队头 now 出队,否则返回 false。

4 从箱子的当前位置开始,向北、南、东、西这 4 个方向扩展。

得到箱子的新位置:nbx=now.bx+dir[i][0];nby=now.by+dir[i][1]。

得到箱子的前一个位置:tx=now.bx-dir[i][0];nby=now.by-dir[i][1]。

如果这两个位置有效,则执行 BFS2 搜素人到达箱子的前一个位置(tx,ty)的最短路径,并记录路径 path。如果 BFS2 搜素成功,则判断是否到达目标,如果是,则返回答案 ans=now.path+path+dpath[i];否则标记箱子的新位置被访问 vis[nbx][nby]=1,将人的新位置(now.bx,now.by)、箱子的新位置(nbx,nby)和已走过的路径(now.path+path+dpathB[i]) 入队。

5 转向步骤 3

六 代码

package com.platform.modules.alg.alglib.poj1475;

import java.util.LinkedList;
import java.util.Queue;

public class Poj1475 {
    public String output = "";

    private int N;
    private int M;
    char mp[][] = new char[25][25];
    // 人和箱子的初始位置
    private int sx;
    private int sy;
    private int bx;
    private int by;

    private final int dir[][] = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
    private final char dpathB[] = {'N', 'S', 'E', 'W'};
    private final char dpathP[] = {'n', 's', 'e', 'w'};
    private String ans;

    public String cal(String input) {
        String[] line = input.split("\n");
        String[] words = line[0].split(" ");
        N = Integer.parseInt(words[0]);
        M = Integer.parseInt(words[1]);
        for (int i = 0; i < N; i++) {
            String mapLine = line[i + 1];
            for (int j = 0; j < mapLine.length(); j++) {
                mp[i][j] = mapLine.charAt(j);
                if (mp[i][j] == 'S') {
                    sx = i;
                    sy = j;
                }

                if (mp[i][j] == 'B') {
                    bx = i;
                    by = j;
                }
            }
        }
        if (bfs())
            output += ans;
        else
            output += "Impossible";
        return output;
    }

    boolean check(int x, int y) {
        if (x < 0 || x >= N || y < 0 || y >= M) return false;
        if (mp[x][y] == '#') return false;
        return true;
    }

    boolean bfs() {
        int vis[][] = new int[25][25];
        vis[bx][by] = 1;
        Queue<node> q = new LinkedList<>(); // 创建一个普通队列(先进先出)
        q.add(new node(sx, sy, bx, by, ""));
        while (!q.isEmpty()) {
            node now = q.peek();
            q.poll();
            for (int i = 0; i < 4; i++) {
                // 箱子的新位置
                int nbx = now.bx + dir[i][0];
                int nby = now.by + dir[i][1];
                // 箱子的前一个位置,人必须能到达这个位置
                int tx = now.bx - dir[i][0];
                int ty = now.by - dir[i][1];
                String path = "";
                StringBuilder pathBuilder = new StringBuilder(path);
                if (check(nbx, nby) && check(tx, ty) && vis[nbx][nby] == 0) {
                    if (bfs2(now.px, now.py, now.bx, now.by, tx, ty, pathBuilder)) {
                        if (mp[nbx][nby] == 'T') {
                            ans = now.path + path + dpathB[i];
                            return true;
                        }
                        vis[nbx][nby] = 1;
                        q.add(new node(now.bx, now.by, nbx, nby, now.path + pathBuilder.toString() + dpathB[i]));
                    }
                }
            }
        }
        return false;
    }

    boolean bfs2(int ppx, int ppy, int bbx, int bby, int tx, int ty, StringBuilder pathBuilder) {
        // 局部标识数组,不要定义全局
        int vis[][] = new int[25][25];
        vis[ppx][ppy] = 1; // 人的位置
        vis[bbx][bby] = 1; // 箱子的位置
        Queue<person> Q = new LinkedList<>(); // 创建一个普通队列(先进先出)
        Q.add(new person(ppx, ppy, ""));
        while (!Q.isEmpty()) {
            person now = Q.peek();
            Q.poll();
            if (now.x == tx && now.y == ty) { // 目标位置,即箱子的前一个位置
                pathBuilder.append(now.path);
                return true;
            }
            for (int i = 0; i < 4; i++) {
                // 人的新位置
                int npx = now.x + dir[i][0];
                int npy = now.y + dir[i][1];
                if (check(npx, npy) && vis[npx][npy] == 0) {
                    vis[npx][npy] = 1;
                    Q.add(new person(npx, npy, now.path + dpathP[i]));
                }
            }
        }
        return false;
    }
}

class person {
    int x;
    int y;
    String path;

    person(int x_, int y_, String path_) {
        x = x_;
        y = y_;
        path = path_;
    }
}

class node {
    int px, py, bx, by;
    String path;


    node(int px_, int py_, int bx_, int by_, String path_) {
        px = px_;
        py = py_;
        bx = bx_;
        by = by_;
        path = path_;
    }
}

七 测试

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
推箱子问题,也称为 Sokoban,是一个经典的益智游戏。在这个游戏中,玩家需要将箱子推到指定的位置,通过推动和移动来达到目标。在计算机科学中,推箱子问题也被用来测试搜索算法和人工智能技术。 C++ 是一种流行的编程语言,可以用来实现推箱子问题的求解算法。其中常用的算法包括深度优先搜索、广度优先搜索、A* 算法等等。 以下是一个简单的 C++ 代码示例,展示了如何实现深度优先搜索算法来解决推箱子问题。请注意,此代码并不完整,仅供参考。 ``` #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 20; int n, m, sx, sy, tx, ty; char g[N][N]; bool st[N][N][N][N]; struct Node { int x, y, px, py; }; int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1}; bool dfs(int x, int y, int px, int py) { if (g[x][y] == 'T') return true; if (st[x][y][px][py]) return false; st[x][y][px][py] = true; for (int i = 0; i < 4; i++) { int a = x + dx[i], b = y + dy[i]; if (g[a][b] == '#') continue; if (a == px && b == py) { int c = a + dx[i], d = b + dy[i]; if (g[c][d] == '#') continue; g[a][b] = '.'; g[c][d] = 'O'; if (dfs(a, b, c, d)) return true; g[a][b] = 'O'; g[c][d] = '.'; } else { if (dfs(a, b, x, y)) return true; } } return false; } int main() { while (cin >> n >> m) { memset(st, 0, sizeof st); memset(g, '#', sizeof g); for (int i = 1; i <= n; i++) { getchar(); for (int j = 1; j <= m; j++) { cin >> g[i][j]; if (g[i][j] == 'S') sx = i, sy = j; if (g[i][j] == 'T') tx = i, ty = j; } } bool success = dfs(sx, sy, -1, -1); if (success) cout << "YES" << endl; else cout << "NO" << endl; } return 0; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值