[HDU 1254] 推箱子 解题报告

原题链接

推箱子

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9254    Accepted Submission(s): 2717

Problem Description
推箱子是一个很经典的游戏.今天我们来玩一个简单版本.在一个M*N的房间里有一个箱子和一个搬运工,搬运工的工作就是把箱子推到指定的位置,注意,搬运工只能推箱子而不能拉箱子,因此如果箱子被推到一个角上(如图2)那么箱子就不能再被移动了,如果箱子被推到一面墙上,那么箱子只能沿着墙移动. 现在给定房间的结构,箱子的位置,搬运工的位置和箱子要被推去的位置,请你计算出搬运工至少要推动箱子多少格.
 

Input
输入数据的第一行是一个整数T(1<=T<=20),代表测试数据的数量.然后是T组测试数据,每组测试数据的第一行是两个正整数M,N(2<=M,N<=7),代表房间的大小,然后是一个M行N列的矩阵,代表房间的布局,其中0代表空的地板,1代表墙,2代表箱子的起始位置,3代表箱子要被推去的位置,4代表搬运工的起始位置.
 

Output
对于每组测试数据,输出搬运工最少需要推动箱子多少格才能帮箱子推到指定位置,如果不能推到指定位置则输出-1.
 

Sample Input
    
    
1 5 5 0 3 0 0 0 1 0 1 4 0 0 0 1 0 0 1 0 2 0 0 0 0 0 0 0
 

Sample Output
   
   
4
 

/*
HDU 推箱子
精简的解题报告 
此题也真算是双向BFS的一个简单模板了(bfs水题?),要点是如何存储人与箱子当前状态的步数最优解,看见m,n的范围想到用数组大法(四维数组)

总结经验:
1.数据不大时涉及到储存状态的,如发现有DP性质优先考虑状态压缩,如没有DP性质优先考虑数组储存而不是结构体(真的是难以驾驭结构体啊)
2.双向bfs特点:有两个动点相关,且一个点的状态由另一点状态决定,两者具有可变性 
下为模板 
*/
#include <iostream>
#include <queue>
#define maxn 10
#define INF 0xfffffff
using namespace std;
//友情提示,bfs的调试难在放置各判断是否压入队列的语句 的 相关位置,发现不了就爆-1,发现了并改正了便可直达病灶 
int n,m,i,j,k,l;//定义能用的全局变量 
int dir[4][2]={0,1,0,-1,-1,0,1,0};//方向 
int vst[maxn][maxn][maxn][maxn];//储存当前状态的step值,并对之后最终的结果作比较得出答案 
int graph[maxn][maxn];//。。。 

struct node{
	int x,y,nx,ny,step;
};

node start;

bool man(node now)//判断人在当前状态下是否满足条件 
{
	return now.x>=0&&now.y>=0&&now.x<m&&now.y<n&&graph[now.x][now.y]!=1&&now.step<vst[now.x][now.y][now.nx][now.ny];//当前step如大于等于原有状态便不考虑压入队列 
}
bool box(node now)
{
	return now.nx>=0&&now.ny>=0&&now.nx<m&&now.ny<n&&graph[now.nx][now.ny]!=1&&now.step<vst[now.x][now.y][now.nx][now.ny];//同上 
}

int bfs()
{
	node now,nex;
	int ans=INF;//记得初始化答案最大值 
	queue <node> Q;

	Q.push(start);
	vst[start.x][start.y][start.nx][start.ny]=1;//开局的起点一定要定义为可操作的,不然爆-1 (人在起点必然会走一步) 
	while(!Q.empty())
	{
		now=Q.front();
		Q.pop();
		if(graph[now.nx][now.ny]==3)
		{
			ans=min(ans,now.step);//到达终点后决定答案 
		}
		for(i=0;i<4;i++)
		{
			nex=now;
			nex.x+=dir[i][0];
			nex.y+=dir[i][1];
			if(man(nex))
			{
				if(nex.x==nex.nx&&nex.y==nex.ny)//如果箱子与人重合,箱子必往人的行走方向(bfs当前方向)再走一步 
				{
					nex.nx+=dir[i][0];
					nex.ny+=dir[i][1];
					nex.step++;//step++不能放在判断box当前位置是否合法后,因为将不满足bool box()的最后一个判断条件 
					if(box(nex))
					{
						vst[nex.x][nex.y][nex.nx][nex.ny]=nex.step;//更新当前状态的最优解 
						Q.push(nex);
					}
				}else//不重合? 
					{
						vst[nex.x][nex.y][nex.nx][nex.ny]=nex.step;//改变原有vst[当前状态]的INF值,防爆-1,step没有++ 
						Q.push(nex);
					}
			}
		}
	}
	return ans;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>m>>n;
		for(i=0; i<maxn; i++)
    	for(j=0; j<maxn; j++)
    	for(k=0; k<maxn; k++)
    	for(l=0; l<maxn; l++)
    		vst[i][j][k][l] = INF;
    	
		for(i=0;i<m;i++)
		{
			for(j=0;j<n;j++)
		{
			cin>>graph[i][j];
			if(graph[i][j]==4)
			{
				start.x=i;start.y=j;start.step=0;
			}else if(graph[i][j]==2)
			{
				start.nx=i;start.ny=j;
			}
		}
		}
		k=bfs();
		if(k==INF)cout<<-1<<endl; 
		else cout<<k<<endl;
	}
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值