题目地址:
https://leetcode.com/problems/minimum-moves-to-move-a-box-to-their-target-location/
给定一个二维地图,例如:
地图以
m
×
n
m\times n
m×n的字符矩阵给出,B
代表箱子的初始位置,T
代表箱子的目标位置,S
代表人的初始位置,#
代表障碍物,人和箱子都不能进入,.
代表空地。除了出界、障碍物以外的位置,人和箱子都可以进入。求将箱子推到目标位置的箱子的最少移动步数。
思路是双端队列BFS。设状态为 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2), ( x 1 , y 1 ) (x_1,y_1) (x1,y1)代表箱子的位置, ( x 2 , y 2 ) (x_2,y_2) (x2,y2)代表人的位置。那么初始状态已知,而其能转移到的状态可以分成两种情况,首先考虑人走一步,那人有三个方向可以走(去掉出界、障碍物等等非法情况),转移的代价为 0 0 0(因为箱子没动),然后再考虑箱子走一步,这种情况只有在人恰好在箱子旁边的时候才能转移到,并且箱子移动到的地方不能非法,这种转移的代价为 1 1 1。那么其实就是在 01 01 01图上求最短路,可以用双端队列BFS来做。代码如下:
class Solution {
public:
struct State {
// box
int x1, y1;
// man
int x2, y2;
};
const int N = 20;
int minPushBox(vector<vector<char>>& g) {
int m = g.size(), n = g[0].size();
State begin;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (g[i][j] == 'B')
begin.x1 = i, begin.y1 = j;
else if (g[i][j] == 'S')
begin.x2 = i, begin.y2 = j;
int dist[N][N][N][N];
bool vis[N][N][N][N];
memset(dist, 0x3f, sizeof dist);
dist[begin.x1][begin.y1][begin.x2][begin.y2] = 0;
memset(vis, 0, sizeof vis);
deque<State> dq;
dq.push_back(begin);
static int d[] = {-1, 0, 1, 0, -1};
while (dq.size()) {
auto t = dq.front();
dq.pop_front();
int x1 = t.x1, y1 = t.y1, x2 = t.x2, y2 = t.y2;
int dis = dist[x1][y1][x2][y2];
if (g[x1][y1] == 'T') return dis;
if (vis[x1][y1][x2][y2]) continue;
vis[x1][y1][x2][y2] = true;
int ni = -1;
for (int i = 0; i < 4; i++) {
int nx2 = x2 + d[i], ny2 = y2 + d[i + 1];
if (0 <= nx2 && nx2 < m && 0 <= ny2 && ny2 < n && g[nx2][ny2] != '#' &&
dist[x1][y1][nx2][ny2] > dis) {
if (nx2 == x1 && ny2 == y1) {
ni = i;
continue;
}
dist[x1][y1][nx2][ny2] = dis;
dq.push_front({x1, y1, nx2, ny2});
}
}
// 如果人不在箱子旁边,则略过第二种转移的情况
if (ni == -1) continue;
int nx1 = x1 + d[ni], ny1 = y1 + d[ni + 1];
if (0 <= nx1 && nx1 < m && 0 <= ny1 && ny1 < n && g[nx1][ny1] != '#' &&
dist[nx1][ny1][x1][y1] > dis + 1) {
dist[nx1][ny1][x1][y1] = dis + 1;
dq.push_back({nx1, ny1, x1, y1});
}
}
return -1;
}
};
时空复杂度 O ( ( m n ) 2 ) O((mn)^2) O((mn)2)。