「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
每日刷题第49天 2021.02.18
688. 骑士在棋盘上的概率
- leetcode原题链接:leetcode-cn.com/problems/kn…
- 难度:中等
- 方法:dfs、动态规划
题目
- 在一个 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 <= 250 <= k <= 1000 <= row, column <= n
总结与拓展
dfs(每次都存在返回值情况,非全局变量)
- 非全局变量需要注意点:每一层dfs遍历完成后记得返回return,此时不仅仅只在终止条件的地方进行返回return。
- (非全局变量)同类型dfs题目1706.球会落何处非常巧妙的练习题,巩固
dfs,加深印象- 球不能落到下一层的几种情况:
- 两个夹角,无法进入到下一层
- 贴左边/右边的墙也无法进入下一层
- 球不能落到下一层的几种情况:
解法
- 根据题目可知,骑士可以走的路有8条,直到其走了k步或者离开了棋盘。计算骑士仍然留在棋盘上的概率。
- 分析:对于骑士走的每一步都需要遍历8个方向,为了便于后续对骑士移动的位置进行处理,将8种情况的
x和y的变量分别存储到两个数组中,如下:let dx = [-2,-2,-1,1,2,2,-1,1];let dy = [-1,1,2,2,-1,1,-2,-2];
- 此时已经解决了骑士走8个方式会到达的位置的单元格下标,那么就需要根据题目所给的
k步,来判断骑士当前的状态,是存在单元格上还是已经跳出棋盘。 - 将
k作为在dfs函数的参数进行传递,这样就可以在每深入一层的时候,k--,直到k == 0那么表示已经递到最深层,可以往回返回了。 - 那么还差一个?如何计算停留在棋盘上的概率呢?
- 令递到最深处的点的概率为1,往上返回一次,则将其值
* 1 / 8。 - 每个节点停留在棋盘上的概率,就等于:其能够递归下去的一层的子节点停留在棋盘上的概率之和。
- 令递到最深处的点的概率为1,往上返回一次,则将其值
- 剪枝操作
- 递归的剪枝,通常都是将已经遍历过的节点获取到的概率值,存储下来,再下次进行遍历的时候,直接取出来返回即可。优化了相同节点的递归操作。
/**
* @param {number} n
* @param {number} k
* @param {number} row
* @param {number} column
* @return {number}
*/
var knightProbability = function(n, k, row, column) {
// 记录每个点的概率是必须的,避免走重复的路,优化
// 8个方向,数组,判断是否出棋盘,没有出棋盘的,给当前还在棋盘上的总数++
// 不需要判断是否走过,只需要取当前的概率进行相乘即可
// 栈中添加,没有走出棋盘的位置,进行下一次的搜索
if(k == 0) return 1;
// 优化放在最后再写
// let step = new Array(n);
// for(let i = 0; i < n; i++) {
// step[i] = new Array(n).fill(1);
// }
// 声明三位数组
let ans = new Array(n);
for(let i = 0; i < n; i++) {
ans[i] = new Array(n);
}
for(let i = 0; i < n; i++){
for(let j = 0; j < n; j++){
ans[i][j] = new Array(101).fill(0);
}
}
// console.log('lal',ans[0][0][2]);
// 问题:怎么知道最终的总概率呢? K
// let stack = [[row, column]];
let dx = [-2,-2,-1,1,2,2,-1,1];
let dy = [-1,1,2,2,-1,1,-2,-2];
function dfs(x,y,step) {
// 当需要走的步数为0时,表示已经到最后的位置
if(step == 0) return 1;
if(ans[x][y][step] != 0) return ans[x][y][step];
// 当前已经遍历过的就不要再遍历了
// if(ans[x][y][step]){
// return ans[x][y][step];
// }
for(let i = 0; i < 8; i++){
// 遍历当前节点的8个方向
let ndx = x + dx[i];
let ndy = y + dy[i];
// 判断当前的数是否合理
if(ndx >= 0 && ndx < n && ndy >= 0 && ndy < n){
ans[x][y][step] += (0.125 * dfs(ndx,ndy,step - 1));
// console.log(x,y,step,'对应',ans[x][y][step]);
}
}
return ans[x][y][step];
// console.log(ans[x][y][step]);
}
// dfs来进行计算概率
dfs(row, column,k);
return ans[row][column][k];
};