Problem: Snakes and Ladders

You are given an `n x n` integer matrix `board` where the cells are labeled
from `1` to `n2` in a Boustrophedon style starting from the bottom left of the
board (i.e. `board[n - 1][0]`) and alternating direction each row.

You start on square `1` of the board. In each move, starting from square
`curr`, do the following:

* Choose a destination square `next` with a label in the range `[curr + 1, min(curr + 6, n2)]`. 
* This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board.
* If `next` has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to `next`.
* The game ends when you reach the square `n2`.

A board square on row `r` and column `c` has a snake or ladder if `board[r][c]
!= -1`. The destination of that snake or ladder is `board[r][c]`. Squares `1`
and `n2` do not have a snake or ladder.

Note that you only take a snake or ladder at most once per move. If the
destination to a snake or ladder is the start of another snake or ladder, you
do not follow the subsequent snake or ladder.

* For example, suppose the board is `[[-1,4],[-1,3]]`, and on the first move, your destination square is `2`. You follow the ladder to square `3`, but do not follow the subsequent ladder to `4`.

Return the least number of moves required to reach the square `n2`. If it is
not possible to reach the square, return `-1`.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def snakesAndLadders(self, board: List[List[int]]) -> int:
    n = len(board)
    ans = 0
    q = collections.deque([1])
    seen = set()
    A = [0] * (1 + n * n)

    for i in range(n):
      for j in range(n):
        if n - i & 1 :
          A[(n - 1 - i) * n + (j + 1)] = board[i][j]
        else:
          A[(n - 1 - i) * n + (n - j)] = board[i][j]

    while q:
      ans += 1
      for _ in range(len(q)):
        curr = q.popleft()
        for next in range(curr + 1, min(curr + 6, n * n) + 1):
          dest = A[next] if A[next] > 0 else next
          if dest == n * n:
            return ans
          if dest in seen:
            continue
          q.append(dest)
          seen.add(dest)

    return -1


Java solution:
class Solution {
  public int snakesAndLadders(int[][] board) {
    final int n = board.length;
    Queue<Integer> q = new ArrayDeque<>(List.of(1));
    boolean[] seen = new boolean[1 + n * n];
    int[] arr = new int[1 + n * n]; // 2D -> 1D

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n; ++j)
        arr[(n - 1 - i) * n + ((n - i) % 2 == 0 ? n - j : j + 1)] = board[i][j];

    for (int step = 1; !q.isEmpty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        final int curr = q.poll();
        for (int next = curr + 1; next <= Math.min(curr + 6, n * n); ++next) {
          final int dest = arr[next] > 0 ? arr[next] : next;
          if (dest == n * n)
            return step;
          if (seen[dest])
            continue;
          q.offer(dest);
          seen[dest] = true;
        }
      }

    return -1;
  }
}


C++ solution:
class Solution {
 public:
  int snakesAndLadders(vector<vector<int>>& board) {
    const int n = board.size();
    queue<int> q{{1}};
    vector<bool> seen(1 + n * n);
    vector<int> arr(1 + n * n);  // 2D -> 1D

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n; ++j)
        arr[(n - 1 - i) * n + ((n - i) % 2 == 0 ? n - j : j + 1)] = board[i][j];

    for (int step = 1; !q.empty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        const int curr = q.front();
        q.pop();
        for (int next = curr + 1; next <= min(curr + 6, n * n); ++next) {
          const int dest = arr[next] > 0 ? arr[next] : next;
          if (dest == n * n)
            return step;
          if (seen[dest])
            continue;
          q.push(dest);
          seen[dest] = true;
        }
      }

    return -1;
  }
};