一个箱子的推箱子算法

该博客讨论了如何解决推箱子问题,通过使用优先队列来找出使箱子到达目标位置所需的最小推动次数。当存在多个达到最小推动次数的解决方案时,优先选择总步数最少的路径。给出了C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Pushing Boxes

Description

Imagine you arestanding inside a two-dimensional maze composed of square cells which may ormay not be filled with rock. You can move north, south, east or west one cellat a step. These moves are called walks. 
One of the empty cells contains a box which can be moved to an adjacent freecell by standing next to the box and then moving in the direction of the box.Such a move is called a push. The box cannot be moved in any other way than bypushing, which means that if you push it into a corner you can never get it outof the corner again. 

One of the empty cells is marked as the target cell. Your job is to bring thebox to the target cell by a sequence of walks and pushes. As the box is veryheavy, you would like to minimize the number of pushes. Can you write a programthat will work out the best such sequence? 

Input

The input containsthe descriptions of several mazes. Each maze description starts with a linecontaining two integers r and c (both <= 20) representing the number of rowsand columns of the maze. 

Following this are r lines each containing c characters. Each characterdescribes one cell of the maze. A cell full of rock is indicated by a `#' andan empty cell is represented by a `.'. Your starting position is symbolized by`S', the starting position of the box by `B' and the target cell by `T'. 

Input is terminated by two zeroes for r and c. 

Output

For each maze inthe input, first print the number of the maze, as shown in the sample output.Then, if it is impossible to bring the box to the target cell, print ``Impossible.''. 

Otherwise, output a sequence that minimizes the number of pushes. If there ismore than one such sequence, choose the one that minimizes the number of totalmoves (walks and pushes). If there is still more than one such sequence, anyone is acceptable. 

Print the sequence as a string of the characters N, S, E, W, n, s, e and wwhere uppercase letters stand for pushes, lowercase letters stand for walks andthe different letters stand for the directions north, south, east and west. 

Output a single blank line after each test case. 

Sample Input

1 7

SB....T

1 7

SB..#.T

7 11

###########

#T##......#

#.#.#..####

#....B....#

#.######..#

#.....S...#

###########

8 4

....

.##.

.#..

.#..

.#.B

.##S

....

###T

0 0

Sample Output

Maze #1

EEEEE

 

Maze #2

Impossible.

 

Maze #3

eennwwWWWWeeeeeesswwwwwwwnNN

 

Maze #4

swwwnnnnnneeesssSSS

 

 

题目意思:很简单,推箱子。给出步骤最少的一种方案即可

解题思路:本题宜采用优先队列,使得步骤越少其优先级越高。关键在于处理son数组的问题。

具体代码如下:

 

#include<iostream>

#include<queue>

usingnamespace std;

 

#definemax 22 //行和列的最大值

 

 

structS

{

    int x,y;//人所在的坐标位置

    int bx,by;//箱子所在的坐标位置

    int id;//唯一标识T

    int pcnt;//人移动的步数

    int bcnt;//箱子移动的步数

    friend bool operator<(struct S a,struct Sb); //运算符重载,用来判断a和b的优先顺序

};

booloperator<(struct S a,struct S b);

voiddfs(int x) ;//递归的方式输出结果

priority_queue<S>que;//存放着符合提议的方案

charvalue[max][max];//存储迷宫的输入

boolisvisit[max][max][max][max];//此四维数组记录的是人的位置和箱子的位置是否走过

S t;

intson[1000000];//记录的是结点先后录入的顺序

charval[1000000];//记录一种行进路线

intmain()

{

    int nexted[4][2];

    nexted[0][0]=0;

    nexted[0][1]=1;//东

    nexted[1][0]=1;

    nexted[1][1]=0;//南

    nexted[2][0]=0;

    nexted[2][1]=-1;//西

    nexted[3][0]=-1;

    nexted[3][1]=0;//北

    int r,c;//r代表迷宫的行,c代表迷宫的列

    cin>>r>>c;

    int tx,ty;//表示箱子的目标位置

    int idx;//递增的id,用来为t的id赋值

    int casenum=1;

    while((r!=0)||(c!=0))

    {

        for(int i=0;i<max;i++)

        {

            for(int j=0;j<max;j++)

            {

                for(int k=0;k<max;k++)

                {

                    for(int t=0;t<max;t++)

                    {

                        isvisit[i][j][k][t]=false;

                    }

                }

            }

        }

        for(int i=0;i<r;i++)

        {

            for(int j=0;j<c;j++)

            {

                cin>>value[i][j];

                if(value[i][j]=='S')

                {

                    value[i][j]= '.';  //此点为可行点

                    t.x=i;

                    t.y=j;//记录下人开始的位置

                }

                if(value[i][j]=='B')

                {

                    value[i][j]='.';//此点为可行点

                    t.bx=i;

                    t.by=j;//记录下箱子开始的位置

                }

                if(value[i][j]=='T')

                {

                    value[i][j]='.';//此点为可行点

                    tx=i;

                    ty=j;//记录目标位置

                }

            }

        }

       

        cout<<"Maze#"<<casenum<<endl;

        casenum+=1;

        isvisit[t.x][t.y][t.bx][t.by]=true;//标记初始点

        t.id=0;//第一个点

        t.pcnt=0;//显然为0

        t.bcnt=0;

        idx=1;//为下个id做准备

        son[0]=-1;//表示结束的标识

        while(!que.empty())

        {

            //清空队列,防止上次的结果对这次的影响

            que.pop();

        }

        que.push(t);

        while(!que.empty())

        {

            t=que.top();

            if((t.bx==tx)&&(t.by==ty))

            {

                //箱子的位置已经在目标位置上了

                dfs(t.id); 

                cout<<endl;

                break;

            }

            //for循环为判断当前点有几种选择

            for(int i=0;i<4;i++)

            {

                //每次移动只有四个选择

                t.x+=nexted[i][0];

                t.y+=nexted[i][1];

                if((t.x>=0)&&(t.x<r)&&(t.y>=0)&&(t.y<c)&&(value[t.x][t.y])=='.')

                {

                    //如果人的下一位置可以行走的话,人有两种选择,1、前进一步,2、推箱子并行进一步

                    if((t.x==t.bx)&&(t.y==t.by))

                    {

                        //箱子和人处于同一位置,就推箱子

                        t.bx+=nexted[i][0];

                        t.by+=nexted[i][1];

                        if((t.bx>=0)&&(t.bx<r)&&(t.by>=0)&&(t.by<c)&&(value[t.bx][t.by]=='.'))

                        {

                            //判断箱子能否推

                            if(!isvisit[t.x][t.y][t.bx][t.by])

                            {

                                //以前没有走过这个节点

                                isvisit[t.x][t.y][t.bx][t.by]=true;

                                //先将下一个可行点入优先队列,然后在恢复当前结点

                                intoldid=t.id; 

                                son[idx]=t.id; 

                                t.id=idx; 

                                t.pcnt++; 

                                t.bcnt++; 

                                if(i==0)val[idx]='E'; 

                                if(i==1)val[idx]='S'; 

                                if(i==2)val[idx]='W'; 

                                if(i==3)val[idx]='N'; 

                                que.push(t); 

                                t.pcnt--; 

                                t.bcnt--; 

                                idx++; 

                                t.id=oldid;

                            }

                        }

                        //箱子回退,寻找其他路径

                        t.bx-=nexted[i][0];

                        t.by-=nexted[i][1];

                    }

                    else

                    {

                        if(!isvisit[t.x][t.y][t.bx][t.by]) 

                        { 

                           isvisit[t.x][t.y][t.bx][t.by]=1; 

                            intoldid=t.id; 

                            son[idx]=t.id; 

                            t.id=idx; 

                            t.bcnt++; 

                            if(i==0)val[idx]='e'; 

                            if(i==1)val[idx]='s'; 

                            if(i==2)val[idx]='w'; 

                            if(i==3)val[idx]='n'; 

                            que.push(t); 

                            t.bcnt--; 

                            idx++; 

                            t.id=oldid;

                        }

                    }

                }

                //人回退

                t.x-=nexted[i][0];

                t.y-=nexted[i][1];

            }

            que.pop();

        }

        if(que.empty())

        {

            cout<<"Impossible."<<endl;

        }

        cout<<endl;

        cin>>r>>c;

    }

    return 0;

}

booloperator<(struct S a,struct S b)

    if(a.pcnt!=b.pcnt) returna.pcnt>b.pcnt; 

    else return a.bcnt>b.bcnt; 

}

voiddfs(int x) 

    if(son[x]!=-1) 

    { 

        dfs(son[x]); 

        cout<<val[x];

    } 

}

´问题描述: 码头仓库是划分为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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值