给你一个大小为 n x n
的整数矩阵 board
,方格按从 1
到 n2
编号,编号遵循转行交替方式 **** , 从左下角开始 (即,从 board[n - 1][0]
开始)每一行交替方向。
玩家从棋盘上的方格 1
(总是在最后一行、第一列)开始出发。
每一回合,玩家需要从当前方格 curr
开始出发,按下述要求前进:
选定目标方格 next
,目标方格的编号符合范围 [curr + 1, min(curr + 6, n2)]
。
该选择模拟了掷 六面体骰子 的情景,无论棋盘大小如何,玩家最多只能有 6 个目的地。
传送玩家:如果目标方格 next
处存在蛇或梯子,那么玩家会传送到蛇或梯子的目的地。否则,玩家传送到目标方格 next
。
当玩家到达编号 n2
的方格时,游戏结束。
r
行 c
列的棋盘,按前述方法编号,棋盘格中可能存在 “蛇” 或 “梯子”;如果 board[r][c] != -1
,那个蛇或梯子的目的地将会是 board[r][c]
。编号为 1
和 n2
的方格上没有蛇或梯子。
注意,玩家在每回合的前进过程中最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,玩家也 不能 继续移动。
举个例子,假设棋盘是 [[-1,4],[-1,3]]
,第一次移动,玩家的目标方格是 2
。那么这个玩家将会顺着梯子到达方格 3
,但 不能 顺着方格 3
上的梯子前往方格 4
。
返回达到编号为 n2
的方格所需的最少移动次数,如果不可能,则返回 -1
。
示例 1:
**输入:** board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]]
**输出:** 4
**解释:**
首先,从方格 1 [第 5 行,第 0 列] 开始。
先决定移动到方格 2 ,并必须爬过梯子移动到到方格 15 。
然后决定移动到方格 17 [第 3 行,第 4 列],必须爬过蛇到方格 13 。
接着决定移动到方格 14 ,且必须通过梯子移动到方格 35 。
最后决定移动到方格 36 , 游戏结束。
可以证明需要至少 4 次移动才能到达最后一个方格,所以答案是 4 。
示例 2:
**输入:** board = [[-1,-1],[-1,3]]
**输出:** 1
提示:
n == board.length == board[i].length
2 <= n <= 20
grid[i][j]
的值是 -1
或在范围 [1, n2]
内
编号为 1
和 n2
的方格上没有蛇或梯子
方法一:广度优先搜索 我们可以将棋盘抽象成一个包含 N^2 个节点的有向图,对于每个节点 x,若 x+i\ (1\le i \le 6) 上没有蛇或梯子,则连一条从 x 到 x+i 的有向边;否则记蛇梯的目的地为 y,连一条从 x 到 y 的有向边。
如此转换后,原问题等价于在这张有向图上求出从 1 到 N^2 的最短路长度。
对于该问题,我们可以使用广度优先搜索。将节点编号和到达该节点的移动次数作为搜索状态,顺着该节点的出边扩展新状态,直至到达终点 N^2,返回此时的移动次数。若无法到达终点则返回 -1。
代码实现时,我们可以用一个队列来存储搜索状态,初始时将起点状态 (1,0) 加入队列,表示当前位于起点 1,移动次数为 0。然后不断取出队首,每次取出队首元素时扩展新状态,即遍历该节点的出边,若出边对应节点未被访问,则将该节点和移动次数加一的结果作为新状态,加入队列。如此循环直至到达终点或队列为空。
此外,我们需要计算出编号在棋盘中的对应行列,以便从 board 中得到目的地。设编号为 id,由于每行有 n 个数字,其位于棋盘从下往上数的第 \left \lfloor \dfrac{\textit{id}-1}{n} \right \rfloor 行,记作 r。由于棋盘的每一行会交替方向,若 r 为偶数,则编号方向从左向右,列号为 (\textit{id}-1) \bmod n;若 r 为奇数,则编号方向从右向左,列号为 n-1-((\textit{id}-1) \bmod n)。
[sol1-C++] 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 class Solution { pair<int , int > id2rc (int id, int n) { int r = (id - 1 ) / n, c = (id - 1 ) % n; if (r % 2 == 1 ) { c = n - 1 - c; } return {n - 1 - r, c}; } public : int snakesAndLadders (vector<vector<int >> &board) { int n = board.size (); vector<int > vis (n * n + 1 ) ; queue<pair<int , int >> q; q.emplace (1 , 0 ); while (!q.empty ()) { auto p = q.front (); q.pop (); for (int i = 1 ; i <= 6 ; ++i) { int nxt = p.first + i; if (nxt > n * n) { break ; } auto rc = id2rc (nxt, n); if (board[rc.first][rc.second] > 0 ) { nxt = board[rc.first][rc.second]; } if (nxt == n * n) { return p.second + 1 ; } if (!vis[nxt]) { vis[nxt] = true ; q.emplace (nxt, p.second + 1 ); } } } return -1 ; } };
[sol1-Java] 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 class Solution { public int snakesAndLadders (int [][] board) { int n = board.length; boolean [] vis = new boolean [n * n + 1 ]; Queue<int []> queue = new LinkedList <int []>(); queue.offer(new int []{1 , 0 }); while (!queue.isEmpty()) { int [] p = queue.poll(); for (int i = 1 ; i <= 6 ; ++i) { int nxt = p[0 ] + i; if (nxt > n * n) { break ; } int [] rc = id2rc(nxt, n); if (board[rc[0 ]][rc[1 ]] > 0 ) { nxt = board[rc[0 ]][rc[1 ]]; } if (nxt == n * n) { return p[1 ] + 1 ; } if (!vis[nxt]) { vis[nxt] = true ; queue.offer(new int []{nxt, p[1 ] + 1 }); } } } return -1 ; } public int [] id2rc(int id, int n) { int r = (id - 1 ) / n, c = (id - 1 ) % n; if (r % 2 == 1 ) { c = n - 1 - c; } return new int []{n - 1 - r, c}; } }
[sol1-C#] 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 public class Solution { public int SnakesAndLadders (int [][] board ) { int n = board.Length; bool [] vis = new bool [n * n + 1 ]; Queue<Tuple<int , int >> queue = new Queue<Tuple<int , int >>(); queue.Enqueue(new Tuple<int , int >(1 , 0 )); while (queue.Count > 0 ) { Tuple<int , int > p = queue.Dequeue(); for (int i = 1 ; i <= 6 ; ++i) { int nxt = p.Item1 + i; if (nxt > n * n) { break ; } Tuple<int , int > rc = Id2Rc(nxt, n); if (board[rc.Item1][rc.Item2] > 0 ) { nxt = board[rc.Item1][rc.Item2]; } if (nxt == n * n) { return p.Item2 + 1 ; } if (!vis[nxt]) { vis[nxt] = true ; queue.Enqueue(new Tuple<int , int >(nxt, p.Item2 + 1 )); } } } return -1 ; } public Tuple<int , int > Id2Rc (int id, int n ) { int r = (id - 1 ) / n, c = (id - 1 ) % n; if (r % 2 == 1 ) { c = n - 1 - c; } return new Tuple<int , int >(n - 1 - r, c); } }
[sol1-Golang] 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 func id2rc (id, n int ) (r, c int ) { r, c = (id-1 )/n, (id-1 )%n if r%2 == 1 { c = n - 1 - c } r = n - 1 - r return } func snakesAndLadders (board [][]int ) int { n := len (board) vis := make ([]bool , n*n+1 ) type pair struct { id, step int } q := []pair{{1 , 0 }} for len (q) > 0 { p := q[0 ] q = q[1 :] for i := 1 ; i <= 6 ; i++ { nxt := p.id + i if nxt > n*n { break } r, c := id2rc(nxt, n) if board[r][c] > 0 { nxt = board[r][c] } if nxt == n*n { return p.step + 1 } if !vis[nxt] { vis[nxt] = true q = append (q, pair{nxt, p.step + 1 }) } } } return -1 }
[sol1-JavaScript] 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 var snakesAndLadders = function (board ) { const n = board.length ; const vis = new Array (n * n + 1 ).fill (0 ); const queue = [[1 , 0 ]]; while (queue.length ) { const p = queue.shift (); for (let i = 1 ; i <= 6 ; ++i) { let nxt = p[0 ] + i; if (nxt > n * n) { break ; } const rc = id2rc (nxt, n); if (board[rc[0 ]][rc[1 ]] > 0 ) { nxt = board[rc[0 ]][rc[1 ]]; } if (nxt === n * n) { return p[1 ] + 1 ; } if (!vis[nxt]) { vis[nxt] = true ; queue.push ([nxt, p[1 ] + 1 ]); } } } return -1 ; }; const id2rc = (id, n ) => { let r = Math .floor ((id - 1 ) / n), c = (id - 1 ) % n; if (r % 2 === 1 ) { c = n - 1 - c; } return [n - 1 - r, c]; }
[sol1-Python3] 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 class Solution : def snakesAndLadders (self, board: List [List [int ]] ) -> int : n = len (board) def id2rc (idx: int ) -> (int , int ): r, c = (idx - 1 ) // n, (idx - 1 ) % n if r % 2 == 1 : c = n - 1 - c return n - 1 - r, c vis = set () q = deque([(1 , 0 )]) while q: idx, step = q.popleft() for i in range (1 , 6 + 1 ): idx_nxt = idx + i if idx_nxt > n * n: break x_nxt, y_nxt = id2rc(idx_nxt) if board[x_nxt][y_nxt] > 0 : idx_nxt = board[x_nxt][y_nxt] if idx_nxt == n * n: return step + 1 if idx_nxt not in vis: vis.add(idx_nxt) q.append((idx_nxt, step + 1 )) return -1
[sol1-C] 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 struct Pair { int first; int second; }; struct Pair id2rc (int id, int n) { int r = (id - 1 ) / n, c = (id - 1 ) % n; if (r % 2 == 1 ) { c = n - 1 - c; } return (struct Pair){n - 1 - r, c}; } int snakesAndLadders (int ** board, int boardSize, int * boardColSize) { int n = boardSize; int vis[n * n + 1 ]; memset (vis, 0 , sizeof (vis)); struct Pair que [n * n ]; int left = 0 , right = 0 ; que[right].first = 1 , que[right++].second = 0 ; while (left < right) { struct Pair p = que[left++]; for (int i = 1 ; i <= 6 ; ++i) { int nxt = p.first + i; if (nxt > n * n) { break ; } struct Pair rc = id2rc(nxt, n); if (board[rc.first][rc.second] > 0 ) { nxt = board[rc.first][rc.second]; } if (nxt == n * n) { return p.second + 1 ; } if (!vis[nxt]) { vis[nxt] = true ; que[right].first = nxt, que[right++].second = p.second + 1 ; } } } return -1 ; }
复杂度分析