[LeetCode] 909. Snakes and Ladders 蛇梯棋
On an N x N `board`, the numbers from `1` to `N*N`are written *boustrophedonically* starting from the bottom left of the board, and alternating direction each row. For example, for a 6 x 6 board, the numbers are written as follows:
You start on square 1
of the board (which is always in the last row and first column). Each move, starting from square x
, consists of the following:
- You choose a destination square
S
with numberx+1
,x+2
,x+3
,x+4
,x+5
, orx+6
, provided this number is<= N*N
.- (This choice simulates the result of a standard 6-sided die roll: ie., there are always at most 6 destinations, regardless of the size of the board.)
- If
S
has a snake or ladder, you move to the destination of that snake or ladder. Otherwise, you move toS
.
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]
.
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 continue moving. (For example, if the board is [[4,-1],[-1,3]]
, and on the first move your destination square is 2
, then you finish your first move at 3
, because you do notcontinue moving to 4
.)
Return the least number of moves required to reach square N*N. If it is not possible, return -1
.
Example 1:
Input: [
[-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]]
Output: 4
Explanation:
At the beginning, you start at square 1 [at row 5, column 0].
You decide to move to square 2, and must take the ladder to square 15.
You then decide to move to square 17 (row 3, column 5), and must take the snake to square 13.
You then decide to move to square 14, and must take the ladder to square 35.
You then decide to move to square 36, ending the game.
It can be shown that you need at least 4 moves to reach the N*N-th square, so the answer is 4.
Note:
2 <= board.length = board[0].length <= 20
board[i][j]
is between1
andN*N
or is equal to-1
.- The board square with number
1
has no snake or ladder. - The board square with number
N*N
has no snake or ladder.
这道题给了一个 NxN 大小的二维数组,从左下角从1开始,蛇形游走,到左上角或者右上角到数字为 NxN,中间某些位置会有梯子,就如同传送门一样,直接可以到达另外一个位置。现在就如同玩大富翁 Big Rich Man 一样,有一个骰子,可以走1到6内的任意一个数字,现在奢侈一把,有无限个遥控骰子,每次都可以走1到6以内指定的步数,问最小能用几步快速到达终点 NxN 位置。博主刚开始做这道题的时候,看是求极值,以为是一道动态规划 Dynamic Programming 的题,结果发现木有办法重现子问题,没法写出状态转移方程,只得作罢。但其实博主忽略了一点,求最小值还有一大神器,广度优先搜索 BFS,最直接的应用就是在迷宫遍历的问题中,求从起点到终点的最少步数,也可以用在更 general 的场景,只要是存在确定的状态转移的方式,可能也可以使用。这道题基本就是类似迷宫遍历的问题,可以走的1到6步可以当作六个方向,这样就可以看作是一个迷宫了,唯一要特殊处理的就是遇见梯子的情况,要跳到另一个位置。这道题还有另一个难点,就是数字标号和数组的二维坐标的转换,这里起始点是在二维数组的左下角,且是1,而代码中定义的二维数组的 (0, 0) 点是在左上角,需要转换一下,还有就是这道题的数字是蛇形环绕的,即当行号是奇数的时候,是从右往左遍历的,转换的时候要注意一下。
难点基本都提到了,现在开始写代码吧,既然是 BFS,就需要用队列 queue 来辅助,初始时将数字1放入,然后还需要一个 visited 数组,大小为 nxn+1。在 while 循环中进行层序遍历,取出队首数字,判断若等于 nxn 直接返回结果 res。否则就要遍历1到6内的所有数字i,则 num+i 就是下一步要走的距离,需要将其转为数组的二维坐标位置,这个操作放到一个单独的子函数中,后边再讲。有了数组的坐标,就可以看该位置上是否有梯子,有的话,需要换成梯子能到达的位置,没有的话还是用 num+i。有了下一个位置,再看 visited 中的值,若已经访问过了直接跳过,否则标记为 true,并且加入队列 queue 中即可,若 while 循环退出了,表示无法到达终点,返回 -1。将数字标号转为二维坐标位置的子函数也不算难,首先应将数字标号减1,因为这里是从1开始的,而代码中的二维坐标是从0开始的,然后除以n得到横坐标,对n取余得到纵坐标。但这里得到的横纵坐标都还不是正确的,因为前面说了数字标记是蛇形环绕的,当行号是奇数的时候,列数需要翻转一下,即用 n-1 减去当前列数。又因为代码中的二维数组起点位置在左上角,同样需要翻转一样,这样得到的才是正确的横纵坐标,返回即可,参见代码如下:
解法一:
class Solution {
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size(), res = 0;
queue<int> q{{1}};
vector<bool> visited(n * n + 1);
while (!q.empty()) {
for (int k = q.size(); k > 0; --k) {
int num = q.front(); q.pop();
if (num == n * n) return res;
for (int i = 1; i <= 6 && num + i <= n * n; ++i) {
auto pos = getPosition(num + i, n);
int next = board[pos[0]][pos[1]] == -1 ? (num + i) : board[pos[0]][pos[1]];
if (visited[next]) continue;
visited[next] = true;
q.push(next);
}
}
++res;
}
return -1;
}
vector<int> getPosition(int num, int n) {
int x = (num - 1) / n, y = (num - 1) % n;
if (x % 2 == 1) y = n - 1 - y;
x = n - 1 - x;
return {x, y};
}
};
我们也可以让子函数直接返回正确的数字标号,而不是数组的二维坐标,这样的写法虽然解题思路和上面都一样,但击败率要更高一些,参见代码如下:
解法二:
class Solution {
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size(), res = 0;
queue<int> q{{1}};
vector<bool> visited(n * n + 1);
while (!q.empty()) {
for (int k = q.size(); k > 0; --k) {
int num = q.front(); q.pop();
if (num == n * n) return res;
for (int i = 1; i <= 6 && num + i <= n * n; ++i) {
int next = getBoardValue(board, num + i);
if (next == -1) next = num + i;
if (visited[next]) continue;
visited[next] = true;
q.push(next);
}
}
++res;
}
return -1;
}
int getBoardValue(vector<vector<int>>& board, int num) {
int n = board.size(), x = (num - 1) / n, y = (num - 1) % n;
if (x % 2 == 1) y = n - 1 - y;
x = n - 1 - x;
return board[x][y];
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/909
参考资料:
https://leetcode.com/problems/snakes-and-ladders/
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 909. Snakes and Ladders 蛇梯棋的更多相关文章
- [Swift]LeetCode909. 蛇梯棋 | Snakes and Ladders
On an N x N board, the numbers from 1 to N*N are written boustrophedonically starting from the botto ...
- 【leetcode】909. Snakes and Ladders
题目如下: 解题思路:天坑题,不在于题目多难,而是要理解题意.题目中有两点要特别注意,一是“You choose a destination square S with number x+1, x+2 ...
- [lightoj P1151] Snakes and Ladders
1151 - Snakes and Ladders Time Limit: 2 second(s) Memory Limit: 32 MB 'Snakes and Ladders' or 'Sh ...
- Snakes and Ladders LightOJ - 1151( 概率dp+高斯消元)
Snakes and Ladders LightOJ - 1151 题意: 有100个格子,从1开始走,每次抛骰子走1~6,若抛出的点数导致走出了100以外,则重新抛一次.有n个格子会单向传送到其他格 ...
- LightOJ - 1151 Snakes and Ladders —— 期望、高斯消元法
题目链接:https://vjudge.net/problem/LightOJ-1151 1151 - Snakes and Ladders PDF (English) Statistics F ...
- [LeetCode] 348. Design Tic-Tac-Toe 设计井字棋游戏
Design a Tic-tac-toe game that is played between two players on a n x n grid. You may assume the fol ...
- [LeetCode] Valid Tic-Tac-Toe State 验证井字棋状态
A Tic-Tac-Toe board is given as a string array board. Return True if and only if it is possible to r ...
- light oj 1151 - Snakes and Ladders 高斯消元+概率DP
思路: 在没有梯子与蛇的时候很容易想到如下公式: dp[i]=1+(∑dp[i+j])/6 但是现在有梯子和蛇也是一样的,初始化p[i]=i; 当有梯子或蛇时转移为p[a]=b; 这样方程变为: dp ...
- LightOJ 1151 Snakes and Ladders(概率DP + 高斯消元)
题意:1~100的格子,有n个传送阵,一个把进入i的人瞬间传送到tp[i](可能传送到前面,也可能是后面),已知传送阵终点不会有另一个传送阵,1和100都不会有传送阵.每次走都需要掷一次骰子(1~6且 ...
随机推荐
- centos 安装python3.7
先安装依赖包: yum -y install bzip2 bzip2-devel ncurses openssl openssl-devel openssl-static xz lzma xz-dev ...
- 机器学习(八)--------支持向量机 (Support Vector Machines)
与逻辑回归和神经网络相比,支持向量机或者简称 SVM,更为强大. 人们有时将支持向量机看作是大间距分类器. 这是我的支持向量机模型代价函数 这样将得到一个更好的决策边界 理解支持向量机模型的做法,即努 ...
- ELK 日志平台 For Windows
一.Logstash 安装 1. 下载最新版本的logstash: https://www.elastic.co/fr/downloads/logstash 下载zip格式的压缩包. 然后解压缩放到 ...
- powershell与linux bash对比
转自Github/Powershell Bash PowerShell Description ls dir, Get-ChildItem List files and folders tree di ...
- 来点高逼格的,使用前端Sendmessage实现SSO
关于什么叫SSO(单点登录),这个概念具体的信息我就不详述了,网上一搜一大把,总的来说,它是一种可以控制各个独立的系统经过它的授权后, 可以用同一个帐号体系登录不同的系统,达到一处登录,多处使用的效果 ...
- STorM32 BGC三轴云台控制板电机驱动电路设计(驱动芯片DRV8313)
1 序言 相信对云台有兴趣的小伙伴对STorM32 BGC这块云台控制板并不陌生,虽说这块控制板的软件已经不再开源,但是在GitHub上依旧可以找到两三个版本的代码,而硬件呢我们也可以从Olliw( ...
- 工作笔记--Python自动切换host
修改host代码: #coding:utf-8import os,time pwd = os.path.dirname(__file__) #获取当前文件夹的绝对路径pull_host_cmd = ' ...
- flux架构的详细介绍和使用!
结构分为四个 视图 view动作 action派发器 dispatcher数据商店 store 流程: 用户操作视图 视图(view)发送动作(action)到派发器(dispatcher) 由派发器 ...
- js 设计模式——策略模式
策略模式(Strategy) 定义:将定义的一组算法封装起来,使其相互之间可以替换.封装的算法具有一定的独立性,不会随客户端的变化而变化 废话不多说,先来个例子 // 例如要写一个计算两个数加减乘除的 ...
- undefined reference to `BN_new'
出现如下错误 undefined reference to `BN_CTX_new' undefined reference to `BN_new' undefined reference to `B ...