要求(递减)
1 箱子走过路程最短
2 人走过路程最短
3 箱子优先按nswe走
4 人优先按nswe走
按照现实状态先考虑箱子怎么走,在此基础之上再想人怎么推
先bfs出箱子的最短路,然后在箱子的每一步中再bfs出人怎么推
箱子每次移动之后人总在箱子移动之前的位置上
把每次箱子移动后,人和箱子的位置看成一个状态
状态表示
( x, y, dir )
x y 为箱子的坐标 dir为人在箱子上下左右的哪个位置
#include<bits/stdc++.h>
using namespace std;
typedef pair< int, int > PII;
const int N = 24;
struct Node
{
int x;
int y;
int dir;
};
int d[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//往上推,人应该在下面
int d_d[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//正常的上下左右
char ops[5] = "nswe";
int n,m;
char g[N][N];
//以箱子刚刚被推动之后为状态,如果人能够推动箱子,
//则人和箱子必定链接在一起组成一个长方形,
//用dir既代表人在箱子新坐标上下左右的哪一个位置上
//也可以表示出箱子旧坐标由哪个方向推到到新坐标
//箱子新坐标为(x,y),人在箱子i方向,箱子是由i方向推过来的
//箱子旧坐标 == 人的新坐标
//为(x + d_d[i][0], y + d_d[i][1])
Node pre[N][N][4]; //存储箱子状态的前驱
int pre_man[N][N];//人由什么方向走来的
vector< int > path[N][N][4]; //人走的路径
PII dist[N][N][4]; //dist[j][k][i]表示箱子从初始坐标到坐标(j,k)人在箱子i方向时的箱子和人的最短路程
bool st[N][N][4],used[N][N];//标记箱/人
bool check( int x, int y )
{
return x >= 0 && x < n && y >= 0 && y < m && g[x][y] != '#';
}
//从起点到终点不经过箱子所需要的最少步骤
int bfs_man( PII start, PII end, PII box, vector<int>&seq )
{
memset( used, false, sizeof used );
memset( pre_man, -1, sizeof pre_man );
queue< PII > q;
q.push( start );
used[start.first][start.second] = true;
used[box.first][box.second] = true;
while( q.size() )
{
auto t = q.front();
q.pop();
int x = t.first;
int y = t.second;
if( t == end )
{
seq.clear();
while( pre_man[x][y] != -1 )
{
int dd = pre_man[x][y];
seq.push_back( dd );
x += d[dd][0];
y += d[dd][1];
}
return seq.size();
}
for( int i = 0; i < 4; i ++ )
{
int a = x + d_d[i][0];
int b = y + d_d[i][1];
if( check( a, b ) && !used[a][b])
{
used[a][b] = true;
pre_man[a][b] = i;
q.push( {a,b} );
}
}
}
return -1;
}
bool bfs_box( PII start, PII box, Node &end )
{
memset( st, false, sizeof st );
queue< Node > q;
for( int i = 0; i < 4; i ++ )
{
int x = box.first;
int y = box.second;
int a = x + d[i][0];
int b = y + d[i][1];//人的初位置
int j = x - d[i][0];
int k = y - d[i][1];//箱子的新位置
vector <int> seq;
if( check( a, b) && check ( j, k) && bfs_man( start, { a, b }, box, seq ) != -1)
{
st[j][k][i] = true;
q.push( { j, k, i } );
dist[j][k][i] = { 1, seq.size() };
path[j][k][i] = seq;
pre[j][k][i] = { x, y, -1};
}
}
bool success=false;
PII man_d={1e9,1e9};
while(q.size()){
Node t=q.front();
q.pop();
if(g[t.x][t.y]=='T')
{
success=true;
if(dist[t.x][t.y][t.dir]<man_d){
end=t;
man_d=dist[t.x][t.y][t.dir];
}
}
for(int i=0;i<4;i++)
{
int a=t.x+d[i][0],b=t.y+d[i][1];
int j=t.x-d[i][0],k=t.y-d[i][1];
if(check(a,b)&&check(j,k))
{//人在的位置和箱子要去的位置是否能走
PII &p=dist[j][k][i];//记录要到达的位置的距离的初始值,使用引用符,方便后面更新
vector<int>seq;//人走的序列
int distance=bfs_man({t.x+d[t.dir][0],t.y+d[t.dir][1]},{a,b},{t.x,t.y},seq);//注意这里人走的位置起点要从前一个的箱子位置还原
if(distance!=-1){//可以走到
PII td={dist[t.x][t.y][t.dir].first+1,dist[t.x][t.y][t.dir].second+distance};//更新走到当前位置的距离
if(!st[j][k][i]){//如果没有到过这个状态
st[j][k][i]=true;
q.push({j,k,i});//将这个状态入队
path[j][k][i]=seq;//记录人具体的走的路径
pre[j][k][i]=t;//这个状态之前是什么状态,方便输出
p=td;//更新到达这个状态的距离
}
else if(p>td){
p=td;
path[j][k][i]=seq;
pre[j][k][i]=t;
}
}
}
}
}
return success;
}
int main()
{
int T = 1;
while( cin >> n >> m, n || m )
{
for( int i = 0; i < n; i ++ )
cin >> g[i];
cout << "Maze #" << T << endl;
T ++;
PII start,box;
for( int i = 0; i < n; i ++ )
for( int j = 0; j < m; j ++ )
{
if( g[i][j] == 'S')
start = { i, j };
else if( g[i][j] == 'B')
box = { i, j };
}
Node end;
string res;
if( !bfs_box( start, box, end ) )
cout<<"Impossible."<<endl;
else
{
while( end.dir != -1)
{
res += ops[end.dir] - 32;
for( int i = 0; i < path[end.x][end.y][end.dir].size(); i ++ )
{
int j = path[end.x][end.y][end.dir][i];
res += ops[j];
}
end = pre[end.x][end.y][end.dir];
}
reverse( res.begin(), res.end() );
cout << res << endl;
}
cout << endl;
}
}


京公网安备 11010502036488号