688. 骑士在棋盘上的概率

688. 骑士在棋盘上的概率

在一个 n x n 的棋盘中,一个骑士从 [row, column] 开始,并尝试进行 k 次移动。行和列是从 0 开始的,所以左上角是 (0,0),右下角是 (n-1, n-1)。每次移动,骑士随机从 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 - 1

func knightProbability(n int, k int, row int, column int) float64 {

}
子问题. 在示例 1 中,我们要解决的问题(原问题)是: 马从 (0,0) 出发,走 k=2 步后仍然在棋盘上的概率。 枚举马走的八个方向,假设走到了 (1,2),问题变成: 马从 (1,2) 出发,走 k−1=1 步后仍然在棋盘上的概率。 这是和原问题相似的、规模更小的子问题,可以用递归解决。

func knightProbability(n int, k int, row int, column int) float64 {
    // dp[i][j][step] 表示在位置 (i,j) 走了 step 步后仍在棋盘上的概率
    // 由于只依赖于上一步,可以使用滚动数组优化
    dirs := [][]int{{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1}}
    
    // 初始化 dp 数组
    prev := make([][]float64, n)
    for i := range prev {
        prev[i] = make([]float64, n)
        for j := range prev[i] {
            prev[i][j] = 1.0
        }
    }
    
    for step := 0; step < k; step++ {
        curr := make([][]float64, n)
        for i := range curr {
            curr[i] = make([]float64, n)
        }
        
        for i := 0; i < n; i++ {
            for j := 0; j < n; j++ {
                for _, dir := range dirs {
                    ni, nj := i+dir[0], j+dir[1]
                    if ni >= 0 && ni < n && nj >= 0 && nj < n {
                        curr[i][j] += prev[ni][nj] / 8.0
                    }
                }
            }
        }
        prev = curr
    }
    
    return prev[row][column]
}