<更新提示>

<第一次更新>


<正文>

Pushing Boxes

Description

Imagine you are standing inside a two-dimensional maze composed of square cells which may or may not be filled with rock. You can move north, south, east or west one cell at a step. These moves are called walks. One of the empty cells contains a box which can be moved to an adjacent free cell 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 by pushing, which means that if you push it into a corner you can never get it out of the corner again.

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

想象一下,你站在一个二维的迷宫里,由方形的格子组成,可能有或者没有被岩石充满。你可以在一个格子上向北、南、东或西移动。这些动作叫做步行。 其中一个空格子包含一个盒子,它可以通过站在盒子旁边,然后在盒子的方向上移动,移动到相邻的空格子。这样的动作叫做推。这个盒子不能用任何其他方式来移动,这意味着如果你把它推到角落里,你就再也不能把它从角落里拿出来。

其中一个空格子被标记为目标格子。你的工作是把箱子放在目标格子中,通过一系列的行走和推动。由于箱子很重,你想把推的数量减到最少。你能写出一个程序来找出最好的顺序吗?

Input Format

迷宫描述都包含一个包含两个整数R和C的线段(R,C<=20R,C<=),表示迷宫的行数和列数。

下面是每个包含C字符的R行。每个字符描述迷宫中的一个单元。一个岩石的格子是由一个'#'表示的,一个空的格子由一个'.'表示。你的起始位置用“S”表示,方框的开始位置由“B”和目标格子按“T”表示。

Output Format

如果不可能把盒子带到目标单元格,打印"Impossible."。

否则,输出一个最小化推送次数的序列。如果有不止一个这样的序列,那么选择一个最小化总移动次数(步行和推送)的序列。如果仍然有不止一个这样的序列,任何一个都是可以接受的。

将序列打印为字符N、S、E、W、n、s、e和w的字符串,其中大写字母代表推,小写字母代表行走,不同字母代表南北、南、东和西的方向。

Sample Input

7 11
###########
#T##......#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########

Sample Output

eennwwWWWWeeeeeesswwwwwwwnNN

解析

这真是一到毒瘤\(bfs\),写了两天,然后校内\(OJ\)因为没有\(SPJ\)所以挂了半天,最后老师推了锅,就由我写了\(SPJ\),真好。

正解应该是双重\(bfs\)。由于题目要求在推箱子次数最小的情况下,人走的步数最小。所以设置状态\((x,y,dir)\)代表箱子在\((x,y)\)位置,人在箱子\(dir\)方向。即人在\((x-dx_{dir},y-dy_{dir})\)的位置。然后对箱子进行\(bfs\),每一次枚举一个方向\(j\),代表人从\((x-dx_{dir},y-dy_{dir})\)位置走到\((x-dx_{j},y-dy_{j})\),然后向\(j\)方向推一下箱子,将箱子推到\((x+dx_j,y+dy_j)\),即拓展到状态\((x+dx_j,y+dy_j,j)\)。

那么怎么将人从\((x-dx_{dir},y-dy_{dir})\)位置走到\((x-dx_{j},y-dy_{j})\)呢,这又是一个朴素\(bfs\)问题,再箱子\(bfs\)的基础上对人执行一遍\(bfs\)即可。

题目还要求输出方案,只要记录每一个状态是由哪一个状态转移过来的,然后得到最优解后用\(while\)循环倒序遍历得到方案即可。

当然,还会有一些小问题,从人的起点走到箱子旁边,显然还要再写一个\(bfs\),到箱子旁边后,对于多个起始状态,我们需要对每一个起始状态都执行一遍从头开始的双重\(bfs\)算法,对于每一次执行双重\(bfs\)算法,到达的第一个目标状态就是最优解。

如果还有问题的话,就是再两次箱子移动时,人如何移动的方案并没有记录,那么就要求我们在得到答案是也要再写一个\(bfs\)重新找到人移动的路径,才能得到方案了。也就是说,算上双重\(bfs\)找最短路径,预处理一个\(bfs\),输出方案一个\(bfs\),总共需要写\(4\)个\(bfs\)函数。

\(tips:\)本文算法可以达到\(0ms\)解决该问题。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define mset(name,val) memset(name,val,sizeof name)
#define filein(str) freopen(str".in","r",stdin)
#define fileout(str) freopen(str".out","w",stdout)
const int N=100,M=100;
const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
struct state
{
int x,y,dir;
inline void clear(void)
{
x=y=dir=0;
}
};
struct node
{
int x,y;
inline void clear(void)
{
x=y=0;
}
};
int CASE,n,m,Map[N][M],cntst,cnted,fcost[10],d[N][M][4];
node man,box,end;
state st[10],ed[10],F[N][M][4];
string prepmove[10],move;
inline bool input(void)
{
scanf("%d%d",&n,&m);
if(!n&&!m)return false;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
char c=' ';
while(isspace(c))c=getchar();
if(c=='#')Map[i][j]=0;
if(c=='.')Map[i][j]=1;
if(c=='S')Map[i][j]=1,man=(node){i,j};
if(c=='B')Map[i][j]=1,box=(node){i,j};
if(c=='T')Map[i][j]=1,end=(node){i,j};
}
}
return true;
}
inline bool check(int x,int y)
{
return x>=1&&y>=1&&x<=n&&y<=m;
}
inline char way(int k)
{
switch(k)
{
case 0:return 'n';
case 1:return 's';
case 2:return 'w';
case 3:return 'e';
}
}
inline void Prepbfs(void)
{
for(int i=0;i<4;i++)
{
int x=end.x-dx[i],y=end.y-dy[i];
if(Map[x][y])
ed[++cnted]=(state){end.x,end.y,i};
}
int dis[N][M];node f[N][M];
mset(dis,-1);
queue < node > q;
q.push(man);
dis[man.x][man.y]=0;f[man.x][man.y]=(node){0,0};
while(!q.empty())
{
node temp=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=temp.x+dx[i],ty=temp.y+dy[i];
if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1)
{
if(tx==box.x&&ty==box.y)
{
st[++cntst]=(state){tx,ty,i};
fcost[cntst]=dis[temp.x][temp.y];
int x=temp.x,y=temp.y;
while(f[x][y].x&&f[x][y].y)
{
for(int j=0;j<4;j++)
if(f[x][y].x+dx[j]==x&&f[x][y].y+dy[j]==y)
prepmove[cntst]+=way(j);
int Tx=x,Ty=y;
x=f[Tx][Ty].x;y=f[Tx][Ty].y;
}
}
else
{
dis[tx][ty]=dis[temp.x][temp.y]+1;
f[tx][ty]=(node){temp.x,temp.y};
q.push((node){tx,ty});
}
}
}
}
}
inline int Expand(node s,node t,node p)
{
if(s.x==t.x&&s.y==t.y)return 0;
int dis[N][M];
mset(dis,-1);
dis[s.x][s.y]=0;
queue < node > q;
q.push(s);
while(!q.empty())
{
node temp=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=temp.x+dx[i],ty=temp.y+dy[i];
if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1&&(tx!=p.x||ty!=p.y))
{
dis[tx][ty]=dis[temp.x][temp.y]+1;
if(tx==t.x&&ty==t.y)return dis[tx][ty];
q.push((node){tx,ty});
}
}
}
return -1;
}
inline bool Arrive(state p)
{
for(int i=1;i<=cnted;i++)
if(ed[i].x==p.x&&ed[i].y==p.y&&ed[i].dir==p.dir)
return true;
return false;
}
inline string Reexband(node s,node t,node p)
{
string res="";
if(s.x==t.x&&s.y==t.y)return res;
int dis[N][M];
node f[N][M];
mset(dis,-1);
dis[s.x][s.y]=0;
f[s.x][s.y]=(node){0,0};
queue < node > q;
q.push(s);
while(!q.empty())
{
node temp=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=temp.x+dx[i],ty=temp.y+dy[i];
if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1&&(tx!=p.x||ty!=p.y))
{
dis[tx][ty]=dis[temp.x][temp.y]+1;
f[tx][ty]=(node){temp.x,temp.y};
if(tx==t.x&&ty==t.y)
{
int x=tx,y=ty;
while(f[x][y].x&&f[x][y].y)
{
for(int j=0;j<4;j++)
if(f[x][y].x+dx[j]==x&&f[x][y].y+dy[j]==y)
res+=way(j);
int Tx=x,Ty=y;
x=f[Tx][Ty].x;y=f[Tx][Ty].y;
}
return res;
}
q.push((node){tx,ty});
}
}
}
}
inline void find(int x,int y,int dir)
{
string temp="";
while(F[x][y][dir].x&&F[x][y][dir].y)
{
for(int i=0;i<4;i++)
{
if(F[x][y][dir].x+dx[i]==x&&F[x][y][dir].y+dy[i]==y)
{
temp+=(char)(way(i)-'a'+'A');
int manendx=F[x][y][dir].x - dx[i];
int manendy=F[x][y][dir].y - dy[i];
int manstx=F[x][y][dir].x - dx[ F[x][y][dir].dir ];
int mansty=F[x][y][dir].y - dy[ F[x][y][dir].dir ];
temp+=Reexband( (node){manstx,mansty} , (node){manendx,manendy} , (node){F[x][y][dir].x,F[x][y][dir].y} );
break;
}
}
int Tx=x,Ty=y,Tdir=dir;
x=F[Tx][Ty][Tdir].x;y=F[Tx][Ty][Tdir].y;dir=F[Tx][Ty][Tdir].dir;
}
for(int i=1;i<=cntst;i++)
if(st[i].x==x&&st[i].y==y&&st[i].dir==dir)
temp+=prepmove[i];
int cnttemp=0,cntmove=0;
for(int i=0;i<temp.size();i++)
if(temp[i]>='A'&&temp[i]<='Z')cnttemp++;
for(int i=0;i<move.size();i++)
if(move[i]>='A'&&move[i]<='Z')cntmove++;
if(move.size()==0||(cnttemp<cntmove)||(cnttemp==cntmove&&temp.size()<move.size()))
move=temp;
}
inline int Bfs(void)
{
int res=-1;
for(int i=1;i<=cntst;i++)
{
queue < state > q;
mset(d,-1);
mset(F,0);
q.push(st[i]);
F[st[i].x][st[i].y][st[i].dir]=(state){0,0,0};
d[st[i].x][st[i].y][st[i].dir]=fcost[i];
while(!q.empty())
{
state temp=q.front();
q.pop();
int x=temp.x,y=temp.y,dir=temp.dir;
if(Arrive(temp))
{
find(x,y,dir);
res=1;
break;
}
for(int j=0;j<4;j++)
{
state t=(state){x+dx[j],y+dy[j],j};
if(!check(t.x,t.y)||!Map[t.x][t.y]||d[t.x][t.y][t.dir]!=-1||!Map[x-dx[j]][y-dy[j]])continue;
int spend=Expand( (node){x-dx[dir],y-dy[dir]} , (node){x-dx[j],y-dy[j]} , (node){x,y} );
if( spend!=-1 )
{
F[t.x][t.y][t.dir]=(state){x,y,dir};
d[t.x][t.y][t.dir]=d[x][y][dir]+spend+1;
q.push(t);
}
}
}
}
return res;
}
int main(void)
{
filein("test");
fileout("test");
input();
Prepbfs();
int ans=Bfs();
if(ans==-1)printf("Impossible.\n");
else
{
for(int i=move.size()-1;i>=0;i--)
printf("%c",move[i]);
puts("\n");
}
return 0;
}

<后记>

『Pushing Boxes 双重bfs』的更多相关文章

  1. poj1475 Pushing Boxes[双重BFS(毒瘤搜索题)]

    地址. 很重要的搜索题.★★★ 吐槽:算是写过的一道码量比较大的搜索题了,细节多,还比较毒瘤.虽然是一遍AC的,其实我提前偷了大数据,但是思路还是想了好长时间,照理说想了半小时出不来,我就会翻题解,但 ...

  2. POJ-1475 Pushing Boxes (BFS+优先队列)

    Description Imagine you are standing inside a two-dimensional maze composed of square cells which ma ...

  3. poj1475 Pushing Boxes(BFS)

    题目链接 http://poj.org/problem?id=1475 题意 推箱子游戏.输入迷宫.箱子的位置.人的位置.目标位置,求人是否能把箱子推到目标位置,若能则输出推的最少的路径,如果有多条步 ...

  4. POJ1475 Pushing Boxes 华丽丽的双重BFS

    woc累死了写了两个半小时...就是BFS?我太菜了... 刚开始以为让人预先跑一遍BFS,然后一会儿取两节加起来就好了,结果发现求出来的最短路(就是这个意思)会因箱子的移动而变化....我死了QWQ ...

  5. poj 1475 || zoj 249 Pushing Boxes

    http://poj.org/problem?id=1475 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=249 Pushin ...

  6. Pushing Boxes(广度优先搜索)

    题目传送门 首先说明我这个代码和lyd的有点不同:可能更加复杂 既然要求以箱子步数为第一关键字,人的步数为第二关键字,那么我们可以想先找到箱子的最短路径.但单单找到箱子的最短路肯定不行啊,因为有时候不 ...

  7. (poj 1475) Pushing Boxes

    Imagine you are standing inside a two-dimensional maze composed of square cells which may or may not ...

  8. [poj P1475] Pushing Boxes

    [poj P1475] Pushing Boxes Time Limit: 2000MS   Memory Limit: 131072K   Special Judge Description Ima ...

  9. POJ1475 Pushing Boxes(双搜索)

    POJ1475 Pushing Boxes  推箱子,#表示墙,B表示箱子的起点,T表示箱子的目标位置,S表示人的起点 本题没有 Special Judge,多解时,先最小化箱子被推动的次数,再最小化 ...

随机推荐

  1. MDK5 设置project targents?如何实现的有知道的请共享一下谢谢感激不尽!!!!

    就在刚刚阅读NRF51822相关的文档时遇到问题,官方给出了一份模板,我从我安装的example中找出了官方的列程,看到是soft config的方式配置的,于是根据列程的配置,自己新建了一个工程之后 ...

  2. python基础day3

    一.文件管理 文件管理是很多应用程序的基本功能和重要组成部分.Python可以使文件管理极其简单,特别是和其它语言相对比. 1.    读操作 1.1r模式 1.1.1读取其他路径下文件 首先在D盘创 ...

  3. setParameter不支持传统的按位置查询方式

    setParameter不支持传统的按位置查询方式 String hql = "from Customer as c where c.cust_id = ?"; List<C ...

  4. HFS 轻量化 的文件服务器

    国外的工具 国内的工具

  5. SpringBoot加Poi仿照EasyPoi实现Excel导出

    POI提供API给Java程序对Microsoft Office格式档案读和写的功能,详细功能可以直接查阅API,因为使用EasyPoi过程中总是缺少依赖,没有搞明白到底是什么坑,索性自己写一个简单工 ...

  6. 洛谷p3801:红色的幻想乡

    初见完全没有思路.....感觉像是线段树 但二维感觉完全不可做嘛 于是只能去看了看题解 然而还是疯狂爆零+WA.. 和yycc神犇调了两三个小时才调出来... ——————以下个人理解 考虑到每次的修 ...

  7. 用JS来判断版本号比如v21.2.2.2和v21.2.2.2.15

    当判断两个版本号的时候,不能单纯的去点然后相互比较,版本比较可能比较特殊,但是分析起来又挺简单的,既然不能直接去点比较,那就拆分成数组,一组一组对应的去比,为了能正常的进行比较 如果上一个版本长度小于 ...

  8. Reveal : Xcode辅助界面调试工具

    Reveal简介: Reveal是一款iOS界面调试工具,辅助Xcode进行界面调试,使用它可以在iOS开发的时候动态的查看和修改应用程序的界面. 软件下载 首先去官网下载Reveal,下载地址:ht ...

  9. vue font-icon 图标

    1.vue 游览器左上角小图标 把.ico文件放在根目录下的static文件夹下,然后link标签引入 <link rel="shortcut icon" href=&quo ...

  10. 二、油泼面(Oil spill noodle)

    油泼面 油泼面是陕西传统的特色面食之一,起源于周代,并以咸阳油泼面最为著名,有鲜香味.酸辣味.香辣味. 油泼面是一种很普通的面食制作方法,将手工制作的面条在开水中煮熟后捞在碗里,将葱花碎.花椒粉.盐等 ...