「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」
前言
今日的题目为中等,题目还是稍微有点难度的,花费了挺多的时间,最后还是看到了别人逆向思维的想法才顺利解决了这道题。
每日一题
今天的每日一题 688. 骑士在棋盘上的概率,难度为中等
-
在一个 n x n 的国际象棋棋盘上,一个骑士从单元格 (row, column) 开始,并尝试进行 k 次移动。行和列是 从 0 开始 的,所以左上单元格是 (0,0) ,右下单元格是 (n - 1, n - 1) 。
-
象棋骑士有8种可能的走法,如下图所示。每次移动在基本方向上是两个单元格,然后在正交方向上是一个单元格。
-
每次骑士要移动时,它都会随机从8种可能的移动中选择一种(即使棋子会离开棋盘),然后移动到那里。
-
骑士继续移动,直到它走了 k 步或离开了棋盘。
-
返回 骑士在棋盘停止移动后仍留在棋盘上的概率 。
示例 1:
输入: n = 3, k = 2, row = 0, column = 0
输出: 0.0625
解释: 有两步(到(1,2),(2,1))可以让骑士留在棋盘上。
在每一个位置上,也有两种移动可以让骑士留在棋盘上。
骑士留在棋盘上的总概率是0.0625。
示例 2:
输入: n = 1, k = 0, row = 0, column = 0
输出: 1.00000
提示:
- 1 <= n <= 25
- 0 <= k <= 100
- 0 <= row, column <= n
题解
动态规划-逆向求解
理解每步操作的概率问题
题目要求从 row , column 出发的骑士停留在棋盘上的概率,对于直接从(row , column) 出发做一个模拟的话,很容易超时,而且要频繁的去判断当前点位是否超出棋盘,以及它是在那一步超出棋盘的(因为题目要求的是概率),在不同步数走出棋盘代表的概率也是不相同的,打个比方:
骑士第一步前往八个点的概率是相同的,并且到了第二布后,前往另外八个点的概率也是相同的,也就是每一次每一个方向的概率都是上一个的 1/8,这里拿 a 点做一个比方,比如第一步去到了 a 点,那么第二步就有八个选择
可以看到第二部会有两步超出了棋盘,那么就是 1/8 的 两份 1/8 的概率 = 1/81/8 + 1/81/8
综合上面所有情况,最后会留在棋盘上的概率就是我们所需要的
逆向思维
那么我们能不能换一种思维,从棋盘上的每一个点作为起点,出发 k 步之后能够到达 (row , column) 的概率有多少。
这里我们需要用到一个三维数组 dp = [step][i][j] 代表在第 [step] 步后到达 (i,j) 的概率。
- 题目要求是走到 k 步之后,所以我们需要初始化出来一个 k+1 大小的 三维数组,并且将他们的值初始化为 0。
const dp = new Array(k + 1).fill(0).map(() => new Array(n).fill(0).map(() => new Array(n).fill(0)));
- 然后我们需要去初始化第零步的情况,也就是当 [step] 为 0 的时候,代表的是不动的情况下的概率,那就肯定是为 1。
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
dp[0][i][j] = 1;
}
}
-
接着,我们从第一步开始去循环之后每一步的情况,每一步都需要去遍历整个棋盘,去计算第几步的时候到达某个点的概率。
-
这里我们每走一步,需要进行是否超出棋盘的判断,我们先定义一个移动的数组,里面是移动的八个方向:
const directions = [[-2, -1], [-2, 1], [2, -1], [2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2]];
然后在每一步进行的时候,要去判断这八个方向是否超出棋盘,不会超出的话,才需要去计算概率
-
在 n 步的时候 (i,j) 上的概率为 n-1 步的时候的所有能走到 (i,j) 的格子的概率的 1/8 相加。
-
结合上面步骤,我们就能够得到这个三维数组,并且返回
dp[k][row][column]在 k 步时走到 (row,column) 概率。
/**
* @param {number} n
* @param {number} k
* @param {number} row
* @param {number} column
* @return {number}
*/
const directions = [[-2, -1], [-2, 1], [2, -1], [2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2]];
var knightProbability = function(n, k, row, column) {
const dp = new Array(k + 1).fill(0).map(() => new Array(n).fill(0).map(() => new Array(n).fill(0)));
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
dp[0][i][j] = 1;
}
}
for (let step = 1; step <= k; step++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
for (const item of directions) {
const x = i + item[0], y = j + item[1];
if (x >= 0 && x < n && y >= 0 && y < n) {
dp[step][i][j] += dp[step - 1][x][y] / 8;
}
}
}
}
}
return dp[k][row][column];
};