题意
题解
如果考虑只有箱子的场景,以坐标 ( x , y ) (x,y) (x,y) 为状态直接 B F S BFS BFS 即可;本题中还要考虑最小化箱子移动步数的情况下最小化人的步数,那么需要考虑人的位置,进一步扩展状态,以保证遍历搜索树。
箱子每一次移动,则人一定位于箱子之前的位置。将相邻的箱子与人看做一个状态,设转移的方向索引为 k k k,那么以 ( x , y , k ) (x,y,k) (x,y,k) 作为状态进行 B F S BFS BFS。每一次状态出队,枚举可能的 4 4 4 个转移方向,那么扩展的下一个状态为 ( x + d x [ i ] , y + d y [ i ] , i ) (x+dx[i],y+dy[i],i) (x+dx[i],y+dy[i],i),对应人的下一个位置为 ( x , y ) (x,y) (x,y),当前人的位置为 ( x − d x [ k ] , y − d y [ k ] ) (x-dx[k],y-dy[k]) (x−dx[k],y−dy[k]),那么状态转移中,人的最小步数需要一次以 ( x , y ) (x,y) (x,y) 为状态、将盒子当前位置看做障碍物的 B F S BFS BFS 进行求解。
状态转移时保证最小化箱子移动步数的情况下最小化人的步数。设目标位置为 ( t x , t y ) (tx,ty) (tx,ty),当 x = t x , y = t y x=tx,y=ty x=tx,y=ty 第一次出队时,由于 B F S BFS BFS 队列的两段性、有序性,保证箱子前两维为 ( t x , t y ) (tx,ty) (tx,ty) 且满足箱子移动步数最小的状态都被更新,而最终状态只考虑位置 ( t x , t y ) (tx,ty) (tx,ty),那么枚举状态的第 3 3 3 维,即方向,满足最小化箱子移动步数的情况下最小化人的步数的状态即答案。于是得到了一个双重 B F S BFS BFS 的算法。 B F S BFS BFS 时记录前驱状态,最后逆推箱子移动轨迹,在相邻的移动进行人的 B F S BFS BFS,最终得到移动方案。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define maxn 22
struct node
{
int x, y, k;
} T, P, B;
const int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
const char ps[4] = {'w', 'e', 'n', 's'}, bs[4] = {'W', 'E', 'N', 'S'};
char mz[maxn][maxn], res[maxn * maxn * maxn * maxn], fst[4][maxn * maxn];
int R, C, dis[maxn][maxn], pm[maxn][maxn][4], bm[maxn][maxn][4], prep[maxn][maxn];
node preb[maxn][maxn][4];
inline bool judge(node& p)
{
return 0 <= p.x && p.x < R && 0 <= p.y && p.y < C && mz[p.x][p.y] != '#';
}
int pbfs(node &p, node &b, node &t)
{
memset(dis, -1, sizeof(dis));
dis[p.x][p.y] = 0;
mz[b.x][b.y] = '#';
queue<node> q;
q.push(p);
while (!q.empty())
{
node cur = q.front();
q.pop();
if (cur.x == t.x && cur.y == t.y)
break;
node nxt;
for (int i = 0; i < 4; ++i)
{
nxt.x = cur.x + dx[i], nxt.y = cur.y + dy[i];
if (judge(nxt) && dis[nxt.x][nxt.y] == -1)
dis[nxt.x][nxt.y] = dis[cur.x][cur.y] + 1, prep[nxt.x][nxt.y] = i, q.push(nxt);
}
}
mz[b.x][b.y] = '.';
return dis[t.x][t.y];
}
void bbfs()
{
memset(pm, -1, sizeof(pm));
memset(bm, -1, sizeof(bm));
queue<node> q;
for (int i = 0; i < 4; ++i)
{
B.k = i;
node t;
t.x = B.x - dx[i], t.y = B.y - dy[i];
int d;
if (judge(t) && (d = pbfs(P, B, t)) != -1)
{
bm[B.x][B.y][B.k] = 0, pm[B.x][B.y][B.k] = d, q.push(B);
for (int k = prep[t.x][t.y]; t.x != P.x || t.y != P.y; k = prep[t.x][t.y])
fst[i][--d] = ps[k], t.x -= dx[k], t.y -= dy[k];
}
}
while (!q.empty())
{
node cur = q.front();
q.pop();
if (cur.x == T.x && cur.y == T.y)
{
for (int i = 0; i < 4; ++i)
if (bm[T.x][T.y][i] == bm[T.x][T.y][cur.k] && pm[T.x][T.y][i] < pm[T.x][T.y][cur.k])
cur.k = i;
int step = bm[cur.x][cur.y][cur.k] + pm[cur.x][cur.y][cur.k];
res[step] = '\0';
for (node tmp = cur;;)
{
res[--step] = bs[tmp.k];
node p, b, t;
b.x = tmp.x - dx[tmp.k], b.y = tmp.y - dy[tmp.k];
t.x = b.x - dx[tmp.k], t.y = b.y - dy[tmp.k];
tmp = preb[tmp.x][tmp.y][tmp.k];
p.x = tmp.x - dx[tmp.k], p.y = tmp.y - dy[tmp.k];
pbfs(p, b, t);
for (int k = prep[t.x][t.y]; t.x != p.x || t.y != p.y; k = prep[t.x][t.y])
res[--step] = ps[k], t.x -= dx[k], t.y -= dy[k];
if (tmp.x == B.x && tmp.y == B.y)
{
while (step)
--step, res[step] = fst[tmp.k][step];
break;
}
}
puts(res);
return;
}
node nxt, tp, sp;
sp.x = cur.x - dx[cur.k], sp.y = cur.y - dy[cur.k];
for (int i = 0; i < 4; ++i)
{
nxt.x = cur.x + dx[i], nxt.y = cur.y + dy[i], nxt.k = i;
tp.x = cur.x - dx[i], tp.y = cur.y - dy[i];
int d;
if (judge(nxt) && (d = pbfs(sp, cur, tp)) != -1)
if (bm[nxt.x][nxt.y][nxt.k] == -1 || (bm[nxt.x][nxt.y][nxt.k] == bm[cur.x][cur.y][cur.k] + 1 && pm[nxt.x][nxt.y][nxt.k] > pm[cur.x][cur.y][cur.k] + d))
bm[nxt.x][nxt.y][nxt.k] = bm[cur.x][cur.y][cur.k] + 1, pm[nxt.x][nxt.y][nxt.k] = pm[cur.x][cur.y][cur.k] + d, preb[nxt.x][nxt.y][nxt.k] = cur, q.push(nxt);
}
}
puts("Impossible.");
}
int main()
{
int m = 0;
while (~scanf("%d%d", &R, &C) && (R | C))
{
for (int i = 0; i < R; ++i)
scanf(" %s", mz[i]);
for (int i = 0; i < R; ++i)
for (int j = 0; j < C; ++j)
if (mz[i][j] == 'S')
P.x = i, P.y = j, mz[i][j] = '.';
else if (mz[i][j] == 'T')
T.x = i, T.y = j, mz[i][j] = '.';
else if (mz[i][j] == 'B')
B.x = i, B.y = j, mz[i][j] = '.';
printf("Maze #%d\n", ++m);
bbfs();
putchar('\n');
}
return 0;
}