1. 题目与解析
我们在一个 n*n 的网格上构建了新的迷宫地图,蛇的长度为 2,也就是说它会占去两个单元格。蛇会从左上角((0, 0) 和 (0, 1))开始移动。我们用 0 表示空单元格,用 1 表示障碍物。蛇需要移动到迷宫的右下角((n-1, n-2) 和 (n-1, n-1))。
每次移动,蛇可以这样走:
- 如果没有障碍,则向右移动一个单元格。并仍然保持身体的水平/竖直状态。
- 如果没有障碍,则向下移动一个单元格。并仍然保持身体的水平/竖直状态。
- 如果它处于水平状态并且其下面的两个单元都是空的,就顺时针旋转 90 度。蛇从(
(r, c)、(r, c+1))移动到 ((r, c)、(r+1, c))。 - 如果它处于竖直状态并且其右面的两个单元都是空的,就逆时针旋转 90 度。蛇从(
(r, c)、(r+1, c))移动到((r, c)、(r, c+1))。
返回蛇抵达目的地所需的最少移动次数。
如果无法到达目的地,请返回 -1。
输入: grid = [[0,0,0,0,0,1], [1,1,0,0,1,0], [0,0,0,0,1,1], [0,0,1,0,1,0], [0,1,1,0,0,0], [0,1,1,0,0,0]]
输出: 11
解释: 一种可能的解决方案是 [右, 右, 顺时针旋转, 右, 下, 下, 下, 下, 逆时针旋转, 右, 下]。
输入: grid = [[0,0,1,1,1,1], [0,0,0,0,1,1], [1,1,0,0,0,1], [1,1,1,0,0,1], [1,1,1,0,0,1], [1,1,1,0,0,0]]
输出: 9
求最短距离,我们可以考虑使用DP来解决问题。
考虑问题,本题目有四种方式可以进行状态转移。对于我们的贪吃蛇而言,有两种存在的状态,一种是横向移动,一种是纵向移动。这两种状态可以进行的状态转移形式也不尽相同。
当贪吃蛇横向移动时,可以进行的两种状态转移方式为:
- 继续横向移动
- 进行转向
当贪吃蛇纵向移动时,可以进行两种状态转移方式:
- 继续纵向运动
- 进行转向
2. 题解
class Solution {
static final int INVALID = Integer.MAX_VALUE / 2;
public int minimumMoves(int[][] grid) {
int n = grid.length;
int[][][] f = new int[n][n][2];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Arrays.fill(f[i][j], INVALID);
}
}
f[0][0][0] = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
boolean canHorizontal = (j + 1 < n && grid[i][j] == 0 && grid[i][j + 1] == 0);
boolean canVertical = (i + 1 < n && grid[i][j] == 0 && grid[i + 1][j] == 0);
if (i - 1 >= 0 && canHorizontal) {
f[i][j][0] = Math.min(f[i][j][0], f[i - 1][j][0] + 1);
}
if (j - 1 >= 0 && canHorizontal) {
f[i][j][0] = Math.min(f[i][j][0], f[i][j - 1][0] + 1);
}
if (i - 1 >= 0 && canVertical) {
f[i][j][1] = Math.min(f[i][j][1], f[i - 1][j][1] + 1);
}
if (j - 1 >= 0 && canVertical) {
f[i][j][1] = Math.min(f[i][j][1], f[i][j - 1][1] + 1);
}
if (canHorizontal && canVertical && grid[i + 1][j + 1] == 0) {
f[i][j][0] = Math.min(f[i][j][0], f[i][j][1] + 1);
f[i][j][1] = Math.min(f[i][j][1], f[i][j][0] + 1);
}
}
}
return (f[n - 1][n - 2][0] == INVALID ? -1 : f[n - 1][n - 2][0]);
}
}