【Leetcode】688. Knight Probability in Chessboard

题目地址:

https://leetcode.com/problems/knight-probability-in-chessboard/

给定一个 N × N N\times N N×N的棋盘,有一个马在坐标 ( r , c ) (r,c) (r,c)上,它可以走 k k k步,问走过 k k k步仍然在棋盘上的概率(中途不能出界)。

法1:动态规划。设 f [ s ] [ i ] [ j ] f[s][i][j] f[s][i][j]是该马从 ( r , c ) (r,c) (r,c)出发,走 s s s步,走到 ( i , j ) (i,j) (i,j)这个位置的概率。那么该马可以从八个方向过来(在不出界的情况下,出界的情况判断一下即可),则有: f [ s ] [ i ] [ j ] = 1 8 ∑ ( x , y ) → ( i , j ) f [ s − 1 ] [ x ] [ y ] f[s][i][j]=\frac{1}{8}\sum_{(x,y)\to (i,j)} f[s-1][x][y] f[s][i][j]=81(x,y)(i,j)f[s1][x][y]边界条件 f [ 0 ] [ r ] [ c ] = 1 f[0][r][c]=1 f[0][r][c]=1 f [ 0 ] f[0] f[0]其余位置都是 0 0 0。最后的答案就是 f [ k ] f[k] f[k]的所有位置数字和。此外,还可以用滚动数组优化空间。代码如下:

public class Solution {
    public double knightProbability(int N, int K, int r, int c) {
        double[][][] dp = new double[2][N][N];
        
        dp[0][r][c] = 1.0;
    
    	// 存八个方向
        int[][] dir = {{1, 2}, {2, 1}, {1, -2}, {2, -1}, {-1, 2}, {-2, 1}, {-1, -2}, {-2, -1}};
        
        for (int step = 1; step <= K; step++) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    dp[step & 1][i][j] = 0;
                    for (int k = 0; k < dir.length; k++) {
                        int prevX = i + dir[k][0], prevY = j + dir[k][1];
                        if (0 <= prevX && prevX < N && 0 <= prevY && prevY < N) {
                            dp[step & 1][i][j] += dp[step - 1 & 1][prevX][prevY];
                        }
                    }
                    
                    dp[step & 1][i][j] *= 0.125;
                }
            }
        }
    
        double res = 0.0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                res += dp[K & 1][i][j];
            }
        }
        
        return res;
    }
}

时间复杂度 O ( k N 2 ) O(kN^2) O(kN2),空间 O ( N 2 ) O(N^2) O(N2)

法2:记忆化搜索。设 f [ s ] [ i ] [ j ] f[s][i][j] f[s][i][j]是从 ( i , j ) (i,j) (i,j)出发,走 s s s步仍然在界内的概率。那么可以按照下一步走到哪儿做分类,每个方向的概率是 1 8 \frac{1}{8} 81,所以按照全概率公式: f [ s ] [ i ] [ j ] = 1 8 ∑ ( i , j ) → ( x , y ) f [ s − 1 ] [ x ] [ y ] f[s][i][j]=\frac{1}{8}\sum_{(i,j)\to (x,y)}f[s-1][x][y] f[s][i][j]=81(i,j)(x,y)f[s1][x][y]边界条件是 f [ 0 ] [ i ] [ j ] = 1 f[0][i][j]=1 f[0][i][j]=1,其中 0 ≤ i < N , 0 ≤ j < N 0\le i< N, 0\le j< N 0i<N,0j<N。代码如下:

public class Solution {
    public double knightProbability(int N, int K, int r, int c) {
        // dp[s][i][j]走s步,从(i, j)出发的情况下,走到界内的概率。这个数组作记忆化之用
        double[][][] dp = new double[K + 1][N][N];
        
        int[][] dir = {{1, 2}, {2, 1}, {1, -2}, {2, -1}, {-1, 2}, {-2, 1}, {-1, -2}, {-2, -1}};
        
        return dfs(r, c, N, K, dp, dir);
    }
    
    // 返回走step步,从(x, y)出发的情况下,走到界内的概率
    private double dfs(int x, int y, int N, int step, double[][][] dp, int[][] dir) {
        if (step == 0) {
        	// 做记忆
            dp[0][x][y] = 1.0;
            return 1.0;
        }
        
        // 有记忆,则返回记忆
        if (dp[step][x][y] > 0) {
            return dp[step][x][y];
        }
        
        double p = 0.0;
        for (int i = 0; i < dir.length; i++) {
            int nextX = x + dir[i][0], nextY = y + dir[i][1];
            if (inBound(nextX, nextY, N)) {
                p += dfs(nextX, nextY, N, step - 1, dp, dir);
            }
        }
        
        p *= 0.125;
        // 返回之前做记忆
        dp[step][x][y] = p;
        return p;
    }
    
    private boolean inBound(int x, int y, int N) {
        return 0 <= x && x < N && 0 <= y && y < N;
    }
}

时空复杂度 O ( k N 2 ) O(kN^2) O(kN2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值