链接:LeetCode688
已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。
现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。
如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。
现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。
求移动结束后,“马” 仍留在棋盘上的概率。
相关标签:动态规划
这并不是一道常规的动态规划题。但是如果想到动态规划来解,其转移方程也是不难想的。
首先创建一个三维数组(dp[i][j][k]),其中(dp[i][j][k])表示棋盘位置为(i)行(j)列的旗子经过(k)步后在棋盘上的概率。每一步,棋子共有八种走法,创建一个二维数组,保存这八个走法,每次循环这八步。对于某个点的八个走法中的任意一个,将该点的位置加上八个走法中的值,得到另外一个位置,用1/8 乘于另外位置上一步得到的棋子位于棋盘上的概率。
至于为什么是(dp[i][j][k])呢?为什么我们要将走的步数放在最外面,这也不难想。我们走k步的状态是由走了k-1步时的状态得到的,所以我们想得到在位置((i,j))上概率,肯定需要走了k-1步时的概率,所以要将k放在最外层进行遍历。
其代码如下:
python:
class Solution:
def knightProbability(self, N: int, K: int, r: int, c: int) -> float:
dp = [[[0 for _ in range(K+1)] for _ in range(N)] for _ in range(N)]
for k in range(K+1):
for i in range(N):
for j in range(N):
if k==0:
dp[i][j][k] = 1
else:
for dx,dy in zip([-2,-2,-1,-1,1,1,2,2],[-1,1,-2,2,-2,2,-1,1]):
new_x = i+dx
new_y = j+dy
if new_x >=0 and new_x <N and new_y >=0 and new_y<N:
dp[i][j][k] += 1/8*dp[new_x][new_y][k-1]
return dp[r][c][K]
C++:
class Solution {
public:
double knightProbability(int N, int K, int r, int c) {
vector<vector<vector<double>>> dp(N,vector<vector<double>>(N,vector<double>(K+1,0)));
int direction[][2] = {{-1, -2}, {1, -2}, {-1, 2}, {1, 2}, {-2, -1}, {-2, 1}, {2, -1}, {2, 1}};
for(int k=0;k<K+1;++k){
for(int i=0;i<N;++i){
for(int j =0;j<N;++j){
if(k==0){
dp[i][j][k] = 1.0;
continue;
}
for(int l=0;l<8;++l){
int dx=direction[l][0],dy=direction[l][1];
int new_x = i+dx,new_y=j+dy;
if(new_x>=0 && new_x<N && new_y>=0 && new_y<N)
dp[i][j][k] += 1.0/8.0*dp[new_x][new_y][k-1];
}
}
}
}
return dp[r][c][K];
}
};