原题链接
在一个 n x n 的国际象棋棋盘上,一个骑士从单元格 (row, column) 开始,并尝试进行 k 次移动。行和列是 从 0 开始 的,所以左上单元格是 (0,0) ,右下单元格是 (n - 1, n - 1) 。
象棋骑士有8种可能的走法,如下图所示。每次移动在基本方向上是两个单元格,然后在正交方向上是一个单元格。
每次骑士要移动时,它都会随机从8种可能的移动中选择一种(即使棋子会离开棋盘),然后移动到那里。
骑士继续移动,直到它走了 k 步或离开了棋盘。
返回 骑士在棋盘停止移动后仍留在棋盘上的概率 。
解题思路
先写最暴力直接的递归方法
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
if row < 0 or row >= n or column < 0 or column >= n: return 0
if k == 0: return 1
moves = [(2,1),(2,-1),(-2,1),(-2,-1),(1,2),(-1,2),(1,-2),(-1,-2)]
P = 0
for dx,dy in moves:
P += self.knightProbability(n,k - 1,row + dx,column + dy)
return P / 8
当然,直接这样提交会超时。
观察到递归函数中,变化的参数为
(
k
,
r
o
w
,
c
o
l
u
m
n
)
(k, row, column)
(k,row,column),因此维护一个
k
∗
n
∗
n
k*n*n
k∗n∗n 的三维 dp 表。其中,
d
p
[
s
t
e
p
]
[
i
]
[
j
]
dp[step][i][j]
dp[step][i][j] 从位置
(
i
,
j
)
(i,j)
(i,j) 开始走
s
t
e
p
step
step 步后,停留在棋盘上的概率,即
k
n
i
g
h
t
P
r
o
b
a
b
i
l
i
t
y
(
n
,
s
t
e
p
,
i
,
j
)
knightProbability(n, step, i, j)
knightProbability(n,step,i,j) 的返回值。
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
dp = [[[0] * n for _ in range(n)] for _ in range(k + 1)]
# 初始化
dp[0] = [[1] * n for _ in range(n)]
moves = [(2,1),(2,-1),(-2,1),(-2,-1),(1,2),(-1,2),(1,-2),(-1,-2)]
for step in range(1,k + 1):
for r in range(n):
for c in range(n):
for dx,dy in moves:
if 0 <= r + dx < n and 0 <= c + dy < n:
dp[step][r][c] += 1 / 8 * dp[step - 1][r + dx][c + dy]
return dp[k][row][column]