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

时间:2022-01-09 06:54:59

地址

很重要的搜索题。★★★


吐槽:算是写过的一道码量比较大的搜索题了,细节多,还比较毒瘤。虽然是一遍AC的,其实我提前偷了大数据,但是思路还是想了好长时间,照理说想了半小时出不来,我就会翻题解,但是这次总觉得自己快想出来了结果磕了两个小时才想到正解上。嘛主要还是我太弱了QwQ。


题目内容很简单。给一张图20*20,推箱子,求推动次数最少的路径,有推动次数相同方案时输出走路次数最少的路径。


乍一看这题觉得很棘手。箱子在动,人还要来回跑来跑去,虽然可以感觉出来是搜索题,但怎么搜?DFS效率太低明显不行的,先排除。那就剩BFS。但是,两个对象,还要维护步数和推数两个玩意儿,还是很棘手。模拟了一下人玩推箱子的过程,发现有一个规律:除了第一次人是奔着箱子去的,其他时候总是人推了箱子后要么继续推要么到箱子另一个方向继续推的。所以时时刻刻人和箱子总是黏在一起的,或者走一点路到另一个方向再“黏”上去。这样就可以把人和箱子作为整体看了。然后就有了记录推数的数组。$pus[x][y][dir]$表示人在坐标$(x,y)$处准备推他上/下/左/右边$(dir=0~3)$的箱子前已推的次数,$walk[x][y][dir]$维护当下已行步数。队列维护一个推数单调增的队列以保证最先求得答案。每次取队头先推一下,记下当前已推次数,拓展的时候就枚举方向看可不可以从箱子新位置四周推动。可以就将这种状态压入队尾,准备下次开推。以上是粗略描述。下面记一下诸多细节。

  • 队列存的是三元组x,y,dir. 每次用最新的推动情况来拓展后面的状况。有两种情形:1、继续向前推:没什么好说的;2、到箱子其他方向推:也需判断能不能推动,可不可以到,可以的话就把现在的步数和推数赋值给他,进队。
  • 当考虑到箱子其他方向上推时,因为需要了解此处到彼处的最短路程,每次,箱子的位置不能通行,所以每个点在每次拓展四个方向前要先跑一次图。因为状态最多$20*20*4=1600$个(每个点每个朝向),所以总共跑图不会昊太多时间。
  • 既然提到队列中状态数量不会超过1600,就要避免重复或无效推动。即我推过来又推回去,或者现在推的状态以前已经拓展过了等等。处理方法:拓展时看四个方向的推法有没有被算过,有的话是不是比当前推动的次数少,少的话就不用了。次数相同表明是我现在同一层拓展出来的产物,那就要看谁到这个状态的步行次数少了。如果没算过,直接赋值入队。又因为该状态是在下一层的,这一层所有状态都有可能去更新到他,所以该状态在取出前一定是最优解。这样同时保证了不会推来推去和冗余重复,后面的效仿前面的。
  • 注意队列只是推数单调,步数并不单调。所以我找到目标后要等这一层全拓展完看哪个步数少。
  • 有可能我推了之后的状态以前有过次数更少的,直接不算了(见line48,如转圈)
  • 寻路过程还具体看code。其实框架还基本差不多。
  • 关于输出路径:行走过程不太好记,所以索性不记了,只记箱子推动的路径(code里面的las),las[x][y][dir]表示将要向dir推的当下坐标上一次是在哪儿向哪边推的。这个想想就明白了。中间的行走路径就再跑一边BFS即可,所以是一个推动和走路穿插的串。
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define mst(a,x) memset(a,x,sizeof a)
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=+,INF=0x3f3f3f3f;
const int dx[]={-,,,},dy[]={,,-,};
const char d[]={'N','S','W','E'};
int mp[N][N],dis[N][N],n,m,bx,by,sx,sy,tx,ty;
#define xx x+dx[i]
#define yy y+dy[i]
queue<pii> Q;
inline void find_way(int X,int Y,int bX,int bY){
mp[bX][bY]=;mst(dis,INF);dis[X][Y]=,Q.push(make_pair(X,Y));
while(!Q.empty()){
int x=Q.front().first,y=Q.front().second;Q.pop();
for(register int i=;i<;++i)if(!mp[xx][yy]&&dis[xx][yy]>dis[x][y]+)dis[xx][yy]=dis[x][y]+,Q.push(make_pair(xx,yy));
}
mp[bX][bY]=;
}
#undef xx
#undef yy
int pus[N][N][],walk[N][N][],vis[N][N][],pre[N][N][],flag,ansx,ansy,ansdir,answ;
pair<pii,int> las[N][N][];
inline void start_pushing(){
find_way(sx,sy,bx,by);queue<pair<pii,int> > q;
mst(pus,INF),mst(walk,INF),mst(vis,);answ=INF;ansx=ansy=ansdir=;flag=-;
for(register int i=,xx=bx+dx[],yy=by+dy[];i<;++i,xx=bx+dx[i],yy=by+dy[i])if(dis[xx][yy]<INF&&!mp[bx-dx[i]][by-dy[i]])
q.push(make_pair(make_pair(xx,yy),i^)),pus[xx][yy][i^]=,walk[xx][yy][i^]=dis[xx][yy],las[xx][yy][i^]=make_pair(make_pair(,),);
while(!q.empty()){
int x=q.front().first.first,y=q.front().first.second,dir=q.front().second;q.pop();
int pushx=pus[x][y][dir]+,walkx=walk[x][y][dir];
if(~flag&&pushx>flag)return;if(pus[x+dx[dir]][y+dy[dir]][dir]<pushx)continue;
int kx=x+*dx[dir],ky=y+*dy[dir];
if(kx==tx&&ky==ty){flag=pushx;if(MIN(answ,walkx))ansx=x,ansy=y,ansdir=dir;continue;}
find_way(x+dx[dir],y+dy[dir],kx,ky);
for(register int i=,xx=kx+dx[],yy=ky+dy[];i<;++i,xx=kx+dx[i],yy=ky+dy[i])if(dis[xx][yy]<INF&&!mp[kx-dx[i]][ky-dy[i]])
if(pus[xx][yy][i^]>=pushx&&walk[xx][yy][i^]>=walkx+dis[xx][yy]){
pus[xx][yy][i^]=pushx,walk[xx][yy][i^]=walkx+dis[xx][yy],las[xx][yy][i^]=make_pair(make_pair(x,y),dir);
if(!vis[xx][yy][i^])q.push(make_pair(make_pair(xx,yy),i^)),vis[xx][yy][i^]=;
}
}
}
char ans[];int len,T,ent;pii Pre[N][N];
inline void print_way(int fx,int fy,int lx,int ly,int bX,int bY){
if(fx==lx&&fy==ly)return;
mp[bX][bY]=;mst(dis,INF);dis[fx][fy]=,Pre[fx][fy]=make_pair(,);queue<pii> q;q.push(make_pair(fx,fy));
while(!q.empty()){
int x=q.front().first,y=q.front().second;q.pop();if(x==lx&&y==ly)break;
for(register int i=;i<;++i)if(!mp[x+dx[i]][y+dy[i]]&&dis[x+dx[i]][y+dy[i]]>dis[x][y]+)
dis[x+dx[i]][y+dy[i]]=dis[x][y]+,q.push(make_pair(x+dx[i],y+dy[i])),Pre[x+dx[i]][y+dy[i]]=make_pair(x,y);
}mp[bX][bY]=;int x=Pre[lx][ly].first,y=Pre[lx][ly].second;
while(x)ans[++len]=(x^lx?(x==lx-?'s':'n'):(y==ly-?'e':'w')),lx=x,ly=y,x=Pre[lx][ly].first,y=Pre[lx][ly].second;
}
inline void print(){
len=;while(ansx){
ans[++len]=d[ansdir];
int x=las[ansx][ansy][ansdir].first.first,y=las[ansx][ansy][ansdir].first.second,dir=las[ansx][ansy][ansdir].second;
if(x)print_way(x+dx[dir],y+dy[dir],ansx,ansy,x+(dx[dir]<<),y+(dy[dir]<<));else break;
ansx=x,ansy=y,ansdir=dir;
}
print_way(sx,sy,ansx,ansy,bx,by);
for(register int i=len;i;--i)printf("%c",ans[i]);puts("");puts("");
} int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
while(read(n),read(m),n&&m){
char s[];//if(ent)puts("");else ent=1;
for(register int i=;i<=n;++i){
scanf("%s",s+);mp[i][]=mp[i][m+]=;
for(register int j=;j<=m;++j)switch(s[j]){
case '#':mp[i][j]=;break;
case '.':mp[i][j]=;break;
case 'S':mp[i][j]=,sx=i,sy=j;break;
case 'T':mp[i][j]=,tx=i,ty=j;break;
case 'B':mp[i][j]=,bx=i,by=j;break;
}
}
for(register int j=;j<=m+;++j)mp[][j]=mp[n+][j]=;
start_pushing();printf("Maze #%d\n",++T);if(~flag)print();else printf("Impossible.\n\n");
}
return ;
}