Skip to content

1210. Minimum Moves to Reach Target with Rotations

  • Time: $O(n^2)$
  • Space: $O(n^2)$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
enum class Pos { kHorizontal, kVertical };

class Solution {
 public:
  int minimumMoves(vector<vector<int>>& grid) {
    const int n = grid.size();
    queue<tuple<int, int, Pos>> q{{{0, 0, Pos::kHorizontal}}};
    vector<vector<vector<bool>>> seen(n,
                                      vector<vector<bool>>(n, vector<bool>(2)));
    seen[0][0][static_cast<int>(Pos::kHorizontal)] = true;

    auto canMoveRight = [&](int x, int y, Pos pos) -> bool {
      if (pos == Pos::kHorizontal)
        return y + 2 < n && !grid[x][y + 2];
      return y + 1 < n && !grid[x][y + 1] && !grid[x + 1][y + 1];
    };

    auto canMoveDown = [&](int x, int y, Pos pos) -> bool {
      if (pos == Pos::kVertical)
        return x + 2 < n && !grid[x + 2][y];
      return x + 1 < n && !grid[x + 1][y] && !grid[x + 1][y + 1];
    };

    auto canRotateClockwise = [&](int x, int y, Pos pos) -> bool {
      return pos == Pos::kHorizontal && x + 1 < n && !grid[x + 1][y + 1] &&
             !grid[x + 1][y];
    };

    auto canRotateCounterclockwise = [&](int x, int y, Pos pos) -> bool {
      return pos == Pos::kVertical && y + 1 < n && !grid[x + 1][y + 1] &&
             !grid[x][y + 1];
    };

    for (int step = 0; !q.empty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        const auto [x, y, pos] = q.front();
        q.pop();
        if (x == n - 1 && y == n - 2 && pos == Pos::kHorizontal)
          return step;
        if (canMoveRight(x, y, pos) && !seen[x][y + 1][static_cast<int>(pos)]) {
          q.emplace(x, y + 1, pos);
          seen[x][y + 1][static_cast<int>(pos)] = true;
        }
        if (canMoveDown(x, y, pos) && !seen[x + 1][y][static_cast<int>(pos)]) {
          q.emplace(x + 1, y, pos);
          seen[x + 1][y][static_cast<int>(pos)] = true;
        }
        const Pos newPos =
            pos == Pos::kHorizontal ? Pos::kVertical : Pos::kHorizontal;
        if ((canRotateClockwise(x, y, pos) ||
             canRotateCounterclockwise(x, y, pos)) &&
            !seen[x][y][static_cast<int>(newPos)]) {
          q.emplace(x, y, newPos);
          seen[x][y][static_cast<int>(newPos)] = true;
        }
      }

    return -1;
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
enum Pos { kHorizontal, kVertical }

class Solution {
  public int minimumMoves(int[][] grid) {
    record T(int x, int y, Pos pos) {}
    final int n = grid.length;
    Queue<T> q = new ArrayDeque<>(List.of(new T(0, 0, Pos.kHorizontal)));
    boolean[][][] seen = new boolean[n][n][2];
    seen[0][0][Pos.kHorizontal.ordinal()] = true;

    for (int step = 0; !q.isEmpty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        final int x = q.peek().x;
        final int y = q.peek().y;
        final Pos pos = q.poll().pos;
        if (x == n - 1 && y == n - 2 && pos == Pos.kHorizontal)
          return step;
        if (canMoveRight(grid, x, y, pos) && !seen[x][y + 1][pos.ordinal()]) {
          q.offer(new T(x, y + 1, pos));
          seen[x][y + 1][pos.ordinal()] = true;
        }
        if (canMoveDown(grid, x, y, pos) && !seen[x + 1][y][pos.ordinal()]) {
          q.offer(new T(x + 1, y, pos));
          seen[x + 1][y][pos.ordinal()] = true;
        }
        final Pos newPos = pos == Pos.kHorizontal ? Pos.kVertical : Pos.kHorizontal;
        if ((canRotateClockwise(grid, x, y, pos) || canRotateCounterclockwise(grid, x, y, pos)) &&
            !seen[x][y][newPos.ordinal()]) {
          q.offer(new T(x, y, newPos));
          seen[x][y][newPos.ordinal()] = true;
        }
      }

    return -1;
  }

  private boolean canMoveRight(int[][] grid, int x, int y, Pos pos) {
    if (pos == Pos.kHorizontal)
      return y + 2 < grid.length && grid[x][y + 2] == 0;
    return y + 1 < grid.length && grid[x][y + 1] == 0 && grid[x + 1][y + 1] == 0;
  }

  private boolean canMoveDown(int[][] grid, int x, int y, Pos pos) {
    if (pos == Pos.kVertical)
      return x + 2 < grid.length && grid[x + 2][y] == 0;
    return x + 1 < grid.length && grid[x + 1][y] == 0 && grid[x + 1][y + 1] == 0;
  }

  private boolean canRotateClockwise(int[][] grid, int x, int y, Pos pos) {
    return pos == Pos.kHorizontal && x + 1 < grid.length && grid[x + 1][y + 1] == 0 &&
        grid[x + 1][y] == 0;
  }

  private boolean canRotateCounterclockwise(int[][] grid, int x, int y, Pos pos) {
    return pos == Pos.kVertical && y + 1 < grid.length && grid[x + 1][y + 1] == 0 &&
        grid[x][y + 1] == 0;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from enum import IntEnum


class Pos(IntEnum):
  kHorizontal = 0
  kVertical = 1


class Solution:
  def minimumMoves(self, grid: list[list[int]]) -> int:
    n = len(grid)
    ans = 0
    # the state of (x, y, pos)
    # pos := 0 (horizontal) / 1 (vertical)
    q = collections.deque([(0, 0, Pos.kHorizontal)])
    seen = {(0, 0, Pos.kHorizontal)}

    def canMoveRight(x: int, y: int, pos: Pos) -> bool:
      if pos == Pos.kHorizontal:
        return y + 2 < n and not grid[x][y + 2]
      return y + 1 < n and not grid[x][y + 1] and not grid[x + 1][y + 1]

    def canMoveDown(x: int, y: int, pos: Pos) -> bool:
      if pos == Pos.kVertical:
        return x + 2 < n and not grid[x + 2][y]
      return x + 1 < n and not grid[x + 1][y] and not grid[x + 1][y + 1]

    def canRotateClockwise(x: int, y: int, pos: Pos) -> bool:
      return (pos == Pos.kHorizontal and x + 1 < n and
              not grid[x + 1][y + 1] and not grid[x + 1][y])

    def canRotateCounterclockwise(x: int, y: int, pos: Pos) -> bool:
      return (pos == Pos.kVertical and y + 1 < n and
              not grid[x + 1][y + 1] and not grid[x][y + 1])

    while q:
      for _ in range(len(q)):
        x, y, pos = q.popleft()
        if x == n - 1 and y == n - 2 and pos == Pos.kHorizontal:
          return ans
        if canMoveRight(x, y, pos) and (x, y + 1, pos) not in seen:
          q.append((x, y + 1, pos))
          seen.add((x, y + 1, pos))
        if canMoveDown(x, y, pos) and (x + 1, y, pos) not in seen:
          q.append((x + 1, y, pos))
          seen.add((x + 1, y, pos))
        newPos = Pos.kVertical if pos == Pos.kHorizontal else Pos.kHorizontal
        if ((canRotateClockwise(x, y, pos) or
             canRotateCounterclockwise(x, y, pos)) and
                (x, y, newPos) not in seen):
          q.append((x, y, newPos))
          seen.add((x, y, newPos))
      ans += 1

    return -1