leetcode刷题记录-688. 骑士在棋盘上的概率-动态规划

258 阅读2分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

前言

今日的题目为中等,题目还是稍微有点难度的,花费了挺多的时间,最后还是看到了别人逆向思维的想法才顺利解决了这道题。

每日一题

今天的每日一题 688. 骑士在棋盘上的概率,难度为中等

  • 在一个 n x n 的国际象棋棋盘上,一个骑士从单元格 (row, column) 开始,并尝试进行 k 次移动。行和列是 从 0 开始 的,所以左上单元格是 (0,0) ,右下单元格是 (n - 1, n - 1) 。

  • 象棋骑士有8种可能的走法,如下图所示。每次移动在基本方向上是两个单元格,然后在正交方向上是一个单元格。

image.png

  • 每次骑士要移动时,它都会随机从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) 出发做一个模拟的话,很容易超时,而且要频繁的去判断当前点位是否超出棋盘,以及它是在那一步超出棋盘的(因为题目要求的是概率),在不同步数走出棋盘代表的概率也是不相同的,打个比方:

image.png

骑士第一步前往八个点的概率是相同的,并且到了第二布后,前往另外八个点的概率也是相同的,也就是每一次每一个方向的概率都是上一个的 1/8,这里拿 a 点做一个比方,比如第一步去到了 a 点,那么第二步就有八个选择

image.png

可以看到第二部会有两步超出了棋盘,那么就是 1/8 的 两份 1/8 的概率 = 1/81/8 + 1/81/8

综合上面所有情况,最后会留在棋盘上的概率就是我们所需要的

逆向思维

那么我们能不能换一种思维,从棋盘上的每一个点作为起点,出发 k 步之后能够到达 (row , column) 的概率有多少。

这里我们需要用到一个三维数组 dp = [step][i][j] 代表在第 [step] 步后到达 (i,j) 的概率。

  1. 题目要求是走到 k 步之后,所以我们需要初始化出来一个 k+1 大小的 三维数组,并且将他们的值初始化为 0。
const dp = new Array(k + 1).fill(0).map(() => new Array(n).fill(0).map(() => new Array(n).fill(0)));
  1. 然后我们需要去初始化第零步的情况,也就是当 [step] 为 0 的时候,代表的是不动的情况下的概率,那就肯定是为 1。
for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
        dp[0][i][j] = 1;
    }
}
  1. 接着,我们从第一步开始去循环之后每一步的情况,每一步都需要去遍历整个棋盘,去计算第几步的时候到达某个点的概率。

  2. 这里我们每走一步,需要进行是否超出棋盘的判断,我们先定义一个移动的数组,里面是移动的八个方向:

const directions = [[-2, -1], [-2, 1], [2, -1], [2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2]];

然后在每一步进行的时候,要去判断这八个方向是否超出棋盘,不会超出的话,才需要去计算概率

  1. 在 n 步的时候 (i,j) 上的概率为 n-1 步的时候的所有能走到 (i,j) 的格子的概率的 1/8 相加。

  2. 结合上面步骤,我们就能够得到这个三维数组,并且返回 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];
 };

image.png