要求(递减)
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; } }