一.题目链接:https://leetcode.com/problems/knight-probability-in-chessboard/
二.题目大意:
给定一个N*N的棋盘和一个初始坐标值(r,c),开始时骑士在初始坐标处,骑士会进行移动,并且骑士移动的时候这只能按照如下的移动方式:
即一共有8个可能移动的方向,并且骑士向每个方向移动的概率都是相同的(即$1/8$) 。求骑士移动K步后,还在棋盘之内的概率。
三.题解:
直观来看,这道题可有用DFS和DP,但不知道DFS会不会超超时,所以我就直接用了DP的思路去解决这个问题。具体如下:
定义一个三维double型数组$dp$,其中$dp[k][i][j]$表示骑士从坐标$(i,j)$处开始移动K步后还在棋盘内的概率。很显然,第K步的结果实收第K-1步的结果影响的,而K-1步的结果可能有8种,于是,可以写出状态转移方程:
$$dp[k][i][j] = 1/8(dp[k-1][i - 2][j + 1] + dp[k-1][i - 2][j - 1] + dp[k-1][i - 1][j - 2] + dp[k-1][i - 1][j + 2] + dp[k-1][i + 2][j + 1] + dp[k-1][i + 2][j - 1] + dp[k-1][i + 1][j - 2] + dp[k-1][i + 1][j + 2])$$
并且当K=0的时候,对于棋盘内所有的坐标点的,相应的概率必为1,所以初始状态的方程为:
$$ dp[0][i][j] = 1$$
好了,根据这两个方程,就可以直接写代码了:
class Solution { public: double knightProbability(int N, int K, int r, int c) { double dp[101][26][26];//dp数组,用于存储状态 int direction[][2] = {{-2,1},{-2,-1},{-1,-2},{-1,2},{2,1},{2,-1},{1,-2},{1,2}};//方向向量,表示8个可能的方向 if(K == 0)//特殊输入的判断 return 1.0; if(r < 0 || c < 0 || c >= N || r >= N)//特殊输入的判断 return 0.0; for(int i = 0; i < N; i++)//初始状态 for(int j = 0; j < N; j++) dp[0][i][j] = 1.0; for(int num = 1; num <= K; num++)//注意循环的顺序 for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) { double tmp = 0; for(int l = 0; l < 8; l++)//分别计算8个可能的方向 { int x = i + direction[l][0]; int y = j + direction[l][1]; if(x < 0 || y <0 || x >= N || y >= N)//不符合条件,则进行下次循环 continue; tmp += (1.0 / 8.0)*dp[num - 1][x][y]; } dp[num][i][j] = tmp; } return dp[K][r][c];//返回最终的结果 } };
该算法的时间复杂度为O(k*N^2),空间复杂度为O(K*N^2),应该可以继续优化的,但不想继续做了....
此外,有几点需要注意的是:
1.最外层循环必须是K,这样的话才能保证,在求父问题的时候,相应的子问题已经求出结果了。
2.要对每种可能的方向,判断他们之前是不是还在棋盘内。