测试地址:Pushing Boxes
题目大意:给定一个r*c的矩形地图,里面有一些石头,有石头的地方不能走,你一开始在S点,要将地图中的一个箱子B推到目标点T(玩过推箱子小游戏的同学应该很容易理解,没玩过的话我在这里解释一下,就是当你要往一个方向移动时,箱子正好在你要到达的地方,此时你和你的箱子都要往那个方向移动一步),箱子也不能被推到有石头的地方,求出一个表示路线的字符串,其中小写字母表示普通的移动,大写字母表示推着箱子时的移动,字母表示方向,要求推箱子的次数最少,如果有多解,输出总移动次数最少的,如果还是有多解,任意输出一个。如果不可能推到,输出“Impossible.”。
做法:这道题目看上去比较复杂,我们来梳理一下。首先要确定搜索时要记录的状态,这个很容易能想到:人的位置和箱子的位置。由于题目中要求的最优解,我们还要附加两个值pushes和walk,表示推动箱子的移动数和平常的移动数,pushes优先级比walk高,然后和前面已经搜出的状态做双关键字比较,如果比以前的状态好,就可以加入优先队列中,优先队列的排序规则也是这个规则。输出路线的话,只要记录一个状态的“父节点”,即拓展到该节点并使该节点状态最优的点,如果箱子的位置到达了T,就从这个状态开始反着搜,判断行走的方向即可。怎么判断是否推动了箱子?只要查看箱子的坐标是否移动即可。还有一个地方坑了我一把,就是存储路线的字符数组一定要开大,因为一个点不一定只走一遍或两遍,所以无法轻易判断路线的长度,所以内存足够的情况下,只能开大一点保险。
(这道题的确是好题...学习A*的同学一定要做一做)
(听说还有双BFS的方法?有待学习)
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int r,c,move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int fa[30][30][30][30][4],h[30][30][30][30][2],t=0;
char a[30][30];
bool flag;
struct node {int x,y;} S,B,T;
struct state
{
node s,b;
int pushes,walk;
bool operator < (state a) const
{
if (pushes!=a.pushes) return a.pushes<pushes;
else return a.walk<walk;
}
};
void setbest(state p)
{
h[p.s.x][p.s.y][p.b.x][p.b.y][0]=p.pushes;
h[p.s.x][p.s.y][p.b.x][p.b.y][1]=p.walk;
}
bool best(state p)
{
if (h[p.s.x][p.s.y][p.b.x][p.b.y][0]!=p.pushes) return p.pushes<h[p.s.x][p.s.y][p.b.x][p.b.y][0];
else return p.walk<h[p.s.x][p.s.y][p.b.x][p.b.y][1];
}
bool can(int x,int y)
{
return x>=1&&x<=r&&y>=1&&y<=c&&a[x][y]!='#';
}
void outputpath(int sx,int sy,int g)
{
char r[10000];
flag=1;
int x1=sx,y1=sy,x2=T.x,y2=T.y,j=g;
while(j>0)
{
int fx1,fx2,fy1,fy2;
char tmp='a';
fx1=fa[x1][y1][x2][y2][0];fy1=fa[x1][y1][x2][y2][1];
fx2=fa[x1][y1][x2][y2][2];fy2=fa[x1][y1][x2][y2][3];
if (fx2!=x2||fy2!=y2) tmp='A';
if (x1-fx1==-1) r[j]='N'-'A'+tmp;
if (x1-fx1==1) r[j]='S'-'A'+tmp;
if (y1-fy1==-1) r[j]='W'-'A'+tmp;
if (y1-fy1==1) r[j]='E'-'A'+tmp;
j--;x1=fx1,y1=fy1,x2=fx2,y2=fy2;
}
for(int i=1;i<=g;i++)
printf("%c",r[i]);
}
void AStar()
{
memset(fa,0,sizeof(fa));
memset(h,0x7f,sizeof(h));
priority_queue<state> Q;
state now,next;
now.s=S,now.b=B;now.pushes=now.walk=0;
Q.push(now);
while(!Q.empty())
{
now=Q.top();
Q.pop();
if (now.b.x==T.x&&now.b.y==T.y) {outputpath(now.s.x,now.s.y,now.pushes+now.walk);break;}
for(int i=0;i<=3;i++)
if (can(now.s.x+move[i][0],now.s.y+move[i][1]))
{
next.s.x=now.s.x+move[i][0];
next.s.y=now.s.y+move[i][1];
if (now.b.x==now.s.x+move[i][0]&&now.b.y==now.s.y+move[i][1])
{
if (!can(now.b.x+move[i][0],now.b.y+move[i][1])) continue;
next.b.x=now.b.x+move[i][0];
next.b.y=now.b.y+move[i][1];
next.pushes=now.pushes+1;next.walk=now.walk;
}
else
{
next.b.x=now.b.x;
next.b.y=now.b.y;
next.walk=now.walk+1;next.pushes=now.pushes;
}
if (best(next))
{
fa[next.s.x][next.s.y][next.b.x][next.b.y][0]=now.s.x;
fa[next.s.x][next.s.y][next.b.x][next.b.y][1]=now.s.y;
fa[next.s.x][next.s.y][next.b.x][next.b.y][2]=now.b.x;
fa[next.s.x][next.s.y][next.b.x][next.b.y][3]=now.b.y;
setbest(next);
Q.push(next);
}
}
}
}
int main()
{
while(scanf("%d%d",&r,&c)&&r&&c)
{
t++;
for(int i=1;i<=r;i++)
{
char s[30];
scanf("%s",s);
for(int j=1;j<=c;j++)
{
a[i][j]=s[j-1];
if (s[j-1]=='S') S.x=i,S.y=j;
if (s[j-1]=='B') B.x=i,B.y=j;
if (s[j-1]=='T') T.x=i,T.y=j;
}
}
printf("Maze #%d\n",t);
flag=0;
AStar();
if (!flag) printf("Impossible.");
printf("\n\n");
}
return 0;
}