【算法实验三】--【分支限界法】--推箱子

1326.推箱子

时限:1000ms 内存限制:10000K  总时限:3000ms

描述

绝大多数人都玩过推箱子的游戏,控制一个人将箱子推动到目标位置即获得胜利。现请你编写一个程序,判断将箱子推到目标位置至少需要多少步。

 

输入

推箱子的平面区域为固定大小(10*10),使用10行10列输入推箱子的初始局面。其中,0代表空格,1代表墙,2代表箱子,3代表目标位置,4代表人。
注:游戏中只有一个箱子,一个目标位置,一个人。

 

输出

输出将箱子推到目标位置的最小步数;若箱子不可能被推到目标位置,输出-1。

 

输入样例

0000000000
0000000300
0100000000
0100000000
0101111100
0000010000
0000010000
0020010040
0000010000
0000010000

 

输出样例

34

解析:这个也是一个很经典的广搜和队列解决的问题。但这个跟前面的跳马,二阶魔方相比又有一点不同。前面的那些题都是每步有一个状态,这个状态是自身产生的一个状态,通过某种方法记录下来进行比较即可。但这个题中的推箱子,若没有人推,只需要让箱子自行移动,每步一个状态记录查重就可以,即退化成了老鼠闯迷宫,但是这个箱子是有人推的,还要考虑这个人所处的位置不同导致推箱子方式不一样。而且这个求的是人的最小步数而不是箱子走的步数。

举例子来说,现在这个人推箱子向下走了一步,那么完成之后这个人就位于箱子的上方。下一步要想把箱子向右推,这个人还得花额外的两步走到箱子的左边,还得考虑箱子左边是不是有位置才能过去推。这里就涉及到人走的问题。

好我又回来了!

我get到了解决方法。既然是求人走的步数,那么我们就以人走为基准,人可以向四个方向移动,当移动完后发现自己站在箱子的位置上,那么箱子也向刚刚的移动的方向移动一格即可。当然这里的used和step数组是四维的,分别是人的位置坐标和箱子的位置坐标。详见代码:

#include <iostream>
#include<queue>
#include<string.h>
#include<stdio.h>
using namespace std;
int m[10][10];
struct node{
   int box[2];
   int ren[2];
   int useful;
};
int dirx[4]={0,0,1,-1};
int diry[4]={1,-1,0,0};
int used[10][10][10][10];
int step[10][10][10][10];
int sx,sy;//箱子起始位置
int tx,ty;//箱子终点位置
int ex,ey;
queue<node> q;
void init_readdata()
{
    while(!q.empty())
        q.pop();
    memset(m,0,sizeof(m));
    memset(used,0,sizeof(used));
    memset(step,0,sizeof(step));
    for(int i=0;i<10;i++)
    {
        for(int j=0;j<10;j++)
        {
            char ch;
            cin>>ch;
            m[i][j]=int(ch)-48;
            if(m[i][j]==3)
            {
                tx=i;
                ty=j;
            }
            else if(m[i][j]==2)
            {
                sx=i;
                sy=j;
            }
            else if(m[i][j]==4)
            {
                ex=i;
                ey=j;
            }
        }
        getchar();
    }
    node start;
    start.box[0]=sx;
    start.box[1]=sy;
    start.ren[0]=ex;
    start.ren[1]=ey;
    q.push(start);
    used[sx][sy][ex][ey]=1;
    step[sx][sy][ex][ey]=1;
}
node moveto(node cur,int i)
{
    node n=cur;
    n.ren[0]=cur.ren[0]+dirx[i];
    n.ren[1]=cur.ren[1]+dirx[i];
    n.useful=0;
    if(n.ren[0]==cur.box[0]&&n.ren[1]==cur.box[1])
    {
        n.box[0]=cur.box[0]+dirx[i];
        n.box[1]=cur.box[1]+dirx[i];
    }
    if(n.ren[0]>=0&&n.ren[0]<10&&n.ren[1]>=0&&n.ren[1]<10)
    {
        if(n.box[0]>=0&&n.box[0]<10&&n.box[1]>=0&&n.box[1]<10)
        {
            if(m[n.ren[0]][n.ren[1]]!=1&&m[n.box[0]][n.box[1]]!=1)
               n.useful=1;
        }
    }
    return n;
}
int bfs()
{
    while(!q.empty())
    {
        node cur=q.front();
        q.pop();
        if(cur.box[0]==tx&&cur.box[1]==ty)
            return step[cur.box[0]][cur.box[1]][cur.ren[0]][cur.ren[1]];
        else
        {
            for(int i=0;i<4;i++)
            {
                node res=moveto(cur,i);
                if(res.useful)
                {
                    if(!used[res.box[0]][res.box[1]][res.ren[0]][res.ren[1]])
                    {
                        q.push(res);
                        used[res.box[0]][res.box[1]][res.ren[0]][res.ren[1]]=1;
                        step[res.box[0]][res.box[1]][res.ren[0]][res.ren[1]]=1+step[cur.box[0]][cur.box[1]][cur.ren[0]][cur.ren[1]];
                    }
                }

            }
        }
    }
    return 0;
}
int main()
{
    init_readdata();
    cout<<bfs()<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值