【第十二周】688. Knight Probability in Chessboard

来源:互联网 发布:java 第三方网络请求 编辑:程序博客网 时间:2024/05/28 16:09

原题

On an NxN chessboard, a knight starts at the r-th row and c-th column and attempts to make exactly K moves. The rows and columns are 0 indexed, so the top-left square is (0, 0), and the bottom-right square is (N-1, N-1).A chess knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction.

…….XN28J.png

Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there.The knight continues moving until it has made exactly K moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving.Example:Input: 3, 2, 0, 0Output: 0.0625Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board.From each of those positions, there are also two moves that will keep the knight on the board.The total probability the knight stays on the board is 0.0625.Note:N will be between 1 and 25.K will be between 0 and 100.The knight always initially starts on the board.

LeetCode地址:https://leetcode.com/problems/knight-probability-in-chessboard/description/

解题思路

题目大意:
有一个”Knight“棋子在一个N*N的棋盘上,初始位置为(r,c);棋子的移动方式类似象棋中的”马“。要求的是在棋子行动K步后,还留在棋盘上的概率。(棋子可以跳到棋盘外,但不能再跳回来)

如何设计算法呢?简单的思路:直接从正面开始推,使用递归将每一次移动的位置都进行判定,一直到K次移动结束;结果发现这种算法的复杂度是O(N^2k),很明显这是个指数爆炸的算法,肯定不可取。

从正面出发无法解决问题,我们可以进行一下反向思考:要求的是K步后棋子还留在棋盘上的概率,如果棋子最后留在棋盘上,那么它的位置必定是(0,0)到(N,N)这N^2个格子中的一个;我们如果能够计算出K步后落在某一个格子上的概率,那么把所有的格子的概率求和,就是要求的解。
现在问题就变成了:如何求棋子移动K步后,坐标为(x, y)的概率?对于这种每一次移动都是一个”状态“的问题,我们可以尝试使用动态规划来解决。那么关键点出现了,如何定义”状态“的意义呢?棋子移动k步后,坐标为(x,y),这就可以定义为一个状态,我们用F(k,x,y)来表示。
那么状态与状态之间的关系又是什么呢?很明显可以发现,当前坐标为(x,y),移动了K步的棋子,其第k-1步的坐标是有限制的:必定为从(x,y)可以跳到的8个位置中的一个。这样我们就可以定义状态转移方程: F(k,x,y) = ∑ F(k-1, 坐标集) / 8 .
有了状态与状态转移方程的定义,就可以很轻松地使用动态规划求解了。

代码

class Solution {public:    double knightProbability(int N, int K, int r, int c) {        double arr[625][100];       // arr[i][j] 表示 位置Knight经过j次移动 跳转到位置i上的概率                                                //  位置i 代表坐标(i / N, i % N)        for (int i = 0; i < N*N; i++) {            arr[i][0] = 0;      // 经过0次移动,所有位置均无法到达(初始位置例外),概率为0            for (int j = 1; j <= K; j++) {                arr[i][j] = -1;     // 移动次数大于0,所有概率初始化为-1,代表没有此位置的概率信息            }        }        arr[r*N + c][0] = 1;    //  最初始位置(r,c),经过0次移动,概率为1         double ans = 0;        for (int i = 0; i < N; i++) {            for (int j = 0; j < N; j++) {                ans += query(arr, N, K, i, j);            }        }        return ans;    }    double query(double (&arr)[625][100], int N, int k, int r, int c) {        if (r < 0 || r > N - 1 || c < 0 || c > N - 1) return 0;     // 棋盘外坐标概率为0        if (arr[r*N + c][k] != -1) return arr[r*N + c][k];      // 概率表中有记录,直接返回        double ans = 0;                 // 概率计算        ans += query(arr, N, k - 1, r - 1, c - 2)            + query(arr, N, k - 1, r - 1, c + 2)            + query(arr, N, k - 1, r + 1, c - 2)            + query(arr, N, k - 1, r + 1, c + 2)            + query(arr, N, k - 1, r - 2, c - 1)            + query(arr, N, k - 1, r - 2, c + 1)            + query(arr, N, k - 1, r + 2, c - 1)            + query(arr, N, k - 1, r + 2, c + 1);        ans /= 8;        arr[r*N + c][k] = ans;          //  计算结果保存进概率表        return ans;    }};

总结

1、多维度思考
2、要善用动态规划来解决问题,熟悉状态与状态转移方程的定义

原创粉丝点击