这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战
题目
688. 骑士在棋盘上的概率 - 力扣(LeetCode) (leetcode-cn.com)
解析
停止移动后仍在棋盘上的概率,其实可以换个思路:
- 求至多K步后,走出棋盘的概率。
这里我们由题目可知:
- 每一次的移动,都是完全随机的。也就是说,每种走法的概率都是1/8。
那么,走出棋盘的情况,就是走到单元格外面了。
一种“想当然的”思路是:
- 求出所有可能走出棋盘的情况数,和所有的情况数相除。
这种思路是不正确的。
假设在第一步有7种走法走出棋盘,那么,这个走出的概率就至少是7/8,和上面的情况实际上是不符合的。
原因是:每一步对于最终结果的权重,都是不同的。
为每一步加上权重(1/8)。如果当前格子已在棋盘之外,那么对于这一步必然是100%的概率,因此返回1;反之,如果当前走了K步,则返回0。
对于都不在上述情况中的,为每一步加上1/8的权重,递归求取。
得到以下代码:
static int[][] dirs = {{1,2},{2,1},{1,-2},{2,-1},{-1,-2},{-2,-1},{-1,2},{-2,1}};
public double knightProbability(int n, int k, int row, int column) {
if(row<0 || row>n-1 || column<0 || column>n-1){
return 0;
}
if(k == 0){
return 1;
}
double cur = 0;
for (int[] dir : dirs) {
double nxt = knightProbability(n,k-1,row+dir[0],column+dir[1]);
cur += nxt/8;
}
return cur;
}
结果:超时
对于这种DFS的题目,一般来说的优化方式就是记录来方便后续相同的计算不再进行。
对于这个题目,实际上的状态包括:
- 当前步数K
- 当前位置row,column。
注意到这里的棋盘是N*N的并且8种走法是对称的,因此对于(k,r,c)和(k,c,r),概率是相同的。
那么此时加上一个三维数组,做一下优化:
double[][][] m = null;
public double knightProbability(int n, int k, int row, int column) {
if(m == null){
m = new double[k+1][n][n];
}
//对称性
if(m[k][row][column]!=0) return m[k][row][column];
if(m[k][column][row]!=0) return m[k][column][row];
//...
//更新
m[k][row][column] = cur;
return cur;
}
结果:
执行用时:3 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了31.52%的用户
有了这个数组,那么试一试用迭代而非递归,来节省栈(因为这里的k,row,column就记录了所需要的结果),也可以同样地使用对称的性质来节约一般的空间,此处不再给出代码。