首页 > 其他分享 >【Leetcode 每日一题】688. 骑士在棋盘上的概率

【Leetcode 每日一题】688. 骑士在棋盘上的概率

时间:2024-12-07 11:58:11浏览次数:7  
标签:int double dfs column 688 棋盘 Leetcode dp row

问题背景

在一个 n × n n \times n n×n 的国际象棋棋盘上,一个骑士从单元格 ( r o w , c o l u m n ) (row, column) (row,column) 开始,并尝试进行 k k k 次移动。行和列是 从 0 0 0 开始 的,所以左上单元格是 ( 0 , 0 ) (0,0) (0,0),右下单元格是 ( n − 1 , n − 1 ) (n - 1, n - 1) (n−1,n−1)。
象棋骑士有 8 8 8 种可能的走法,如下图所示。每次移动在基本方向上是两个单元格,然后在正交方向上是一个单元格。
Leetcode 688. 骑士在棋盘上的概率

每次骑士要移动时,它都会随机从 8 8 8 种可能的移动中选择一种(即使棋子会离开棋盘),然后移动到那里。
骑士继续移动,直到它走了 k k k 步或离开了棋盘。
返回 骑士在棋盘停止移动后仍留在棋盘上的概率 。

数据约束

  • 1 ≤ n ≤ 25 1 \le n \le 25 1≤n≤25
  • 0 ≤ k ≤ 100 0 \le k \le 100 0≤k≤100
  • 0 ≤ r o w , c o l u m n ≤ n − 1 0 \le row, column \le n - 1 0≤row,column≤n−1

解题过程

自己写出了按方向的回溯,但是没有应用记忆化搜索导致超时。不过即使记录的中间过程的结果,答案也是不对的,因为考虑的是求最终所有的有效落点与所有可能落点的比值。事实上递归的过程中,某个状态是依赖于前一个状态的,要计算的是分阶段的概率。
最终记录答案时需要理解两个细节:

  • 在不进行更深层次的递归时,当前的操作只可能是八选一。这种情况下概率是可以直接求和的,因为分母相同,等效于有效结果数除以所有可能结果数。
  • 上一轮的概率会在记录并返回时被稀释(除以八),符合多步计算使用乘法公式的条件。

其它参考 灵神题解:定义状态 d f s ( k , i , j ) dfs(k, i, j) dfs(k,i,j),表示从 ( i , j ) (i, j) (i,j) 出发走 k k k 步后仍在棋盘上的概率。若从 ( i , j ) (i, j) (i,j) 出发,有 1 8 \frac {1} {8} 81​ 的概率走向 ( x , y ) (x, y) (x,y),那么原问题与子问题的关系就是 d f s ( k , i , j ) = 1 8 Σ ( x , y ) d f s ( k − 1 , x , y ) dfs(k, i, j) = \frac {1} {8} \mathop{\Sigma}\limits_{(x, y)} dfs(k - 1, x, y) dfs(k,i,j)=81​(x,y)Σ​dfs(k−1,x,y)。
递归入口 是 d f s ( k , r o w , c o l u m n ) dfs(k, row, column) dfs(k,row,column),也是答案。
递归边界 是当前位置仍在棋盘上,则 d f s ( k , i , j ) = 1 dfs(k, i, j) = 1 dfs(k,i,j)=1;当前位置出界,则 d f s ( k , i , j ) = 1 dfs(k, i, j) = 1 dfs(k,i,j)=1。

递归的做法实现完成之后,可以把它等效地翻译成递推。
考虑到棋盘上出界也会导致下标越界,而棋子最大的可能越界范围是边界以外距离为 2 2 2的位置,所以可以在初始化数组的时候在上下左右四个方向各扩展 2 2 2 个单位,通过偏移来修正表达式。
答案为 d p [ k ] [ r o w + 2 ] [ c o l u m n + 2 ] dp[k][row + 2][column + 2] dp[k][row+2][column+2]。
初始值 d p [ 0 ] [ i ] [ j ] dp[0][i][j] dp[0][i][j] 表示尚未移动时,棋子一定在棋盘上。

目前总结出来的是,递归转换成递推时,表征递归层数的变量会在循环的最外层。

具体实现

递归

class Solution {
    private static final int[][] DIRECTIONS = new int[][] {{-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}};

    public double knightProbability(int n, int k, int row, int column) {
        double[][][] memo = new double[k + 1][n][n];
        return dfs(n, k, row, column, memo); 
    }

    private double dfs(int n, int k, int i, int j, double[][][] memo) {
        if(i < 0 || i >= n || j < 0 || j >= n) {
            return 0;
        }
        if(k == 0) {
            return 1;
        }
        if(memo[k][i][j] != 0) {
            return memo[k][i][j];
        }
        double res = 0.0;
        for(int[] direction : DIRECTIONS) {
            // 同层的概率可累加
            res += dfs(n, k - 1, i + direction[0], j + direction[1], memo);
        }
        // 进入下一层之前计算最终结果,实际上也进行了稀释
        return memo[k][i][j] = res / DIRECTIONS.length;
    }
}

非递归

class Solution {
    private static final int[][] DIRECTIONS = new int[][] {{-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}};

    public double knightProbability(int n, int k, int row, int column) {
        double[][][] dp = new double[k + 1][n + 4][n + 4];
        for(int i = 2; i < n + 2; i++) {
            Arrays.fill(dp[0][i], 2, n + 2, 1);
        }
        for(int step = 1; step <= k; step++) {
            for(int i = 2; i < n + 2; i++) {
                for(int j = 2; j < n + 2; j++) {
                    for(int[] direction : DIRECTIONS) {
                        dp[step][i][j] += dp[step - 1][i + direction[0]][j + direction[1]];
                    }
                    dp[step][i][j] /= DIRECTIONS.length;
                }
            }
        }
        return dp[k][row + 2][column + 2];
    }
}

标签:int,double,dfs,column,688,棋盘,Leetcode,dp,row
From: https://blog.csdn.net/2401_88859777/article/details/144307909

相关文章

  • LeetCode 263[丑数]
    题目链接LeetCode263[丑数]详情实例提示题解思考题目对丑数的定义:只包含质因数2、3、5的正整数条件一:只包含质因数2、3、5条件二:正整数 对于条件二很好筛选:如果给定值n小于1,即给定值为0或者是负数,此时条件二不满足,则返回false该部分的代码实现如下:......
  • leetcode3288 最长上升路径的长度
    给定长度为n的二维数组{x[i],y[i]}和一个整数k,其中0<=k<n,从中选中若干个点排序构成序列,求最长的点序列满足x[i]<x[i+1]并且y[i]<y[i+1],要求第k个点必须选择,返回最长序列的长度。1<=n<=1E5;0<=x[i],y[i]<=1E9;各个点互不相同分析:可以拆分成如下几个子任务:(1)求一维数组的lis的长度......
  • leetcode-1193. 每月交易 I
     建表语句:CreatetableIfNotExistsTransactions(idint,countryvarchar(4),stateenum('approved','declined'),amountint,trans_datedate)TruncatetableTransactionsinsertintoTransactions(id,country,state,amount,trans_date)......
  • leetcode 3266. K 次乘运算后的最终数组 II
    3266.K次乘运算后的最终数组II给你一个整数数组 nums ,一个整数 k  和一个整数 multiplier 。你需要对 nums 执行 k 次操作,每次操作中:找到 nums 中的 最小 值 x ,如果存在多个最小值,选择最 前面 的一个。将 x 替换为 x*multiplier 。k 次操作以......
  • leetcode 3. 无重复字符的最长子串
    3.无重复字符的最长子串给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。 滑动窗口模板//外层循环扩展右边界,内层循环扩展左边界for(intl=0,r=0;r<n;r++){//当前考虑的元素while(l<=r&&check()){//区间[left,right]不符......
  • leetcode第4题 如何求出两个有序数组的中位数
    leetcode原题大意,给定两个升序排列的有序数组,例如nums1=[1,2],nums2=[3,4]那么,这两个有序数组的所有数字的中位数为(2+3)/2=1.5,现在要求以O(log(m+n))的时间复杂度。funcfindMedianSortedArrays(nums1[]int,nums2[]int)float64{ length:=len(nums1)+len(nums2) ......
  • 2024/12/6 【哈希表】LeetCode1.两数之和 【√】
    解法1:暴力解法classSolution:deftwoSum(self,nums:List[int],target:int)->List[int]:foriinrange(len(nums)):des=target-nums[i]ifdesinnums:forjinrange(len(nums)):......
  • LeetCode LCR072[x的平方根]
    题目链接LeetCodeLCR072[x的平方根]详情实例提示题解思路一[暴力法]由于所求的是整型且是正符号整型,可以采取循环遍历的方式来求取平方根用for循环将i由0开始遍历循环体:求i的平方值当平方值小于指定值,此时循环继续退出循环的条件:当平方值为指定值时,返回......
  • leetcode 2056. 棋盘上有效移动组合的数目
    classSolution{private:  vector<vector<int>>RMove={{1,0},{-1,0},{0,1},{0,-1}};  vector<vector<int>>BMove={{1,1},{-1,-1},{-1,1},{1,-1}};public:  boolCheckMove(intsx,intsy,intx,inty,intstep,vector<vector......
  • leetcode2836 在传球游戏中最大化函数值
    n名玩家在玩传球游戏,编号为i的玩家固定会把球传给编号为r[i]的玩家,任选一名玩家开始传球,恰好传k次,得分为这k次传球内所有接触过球的玩家的编号之和,如果玩家多次触球,则累加多次。问从哪个玩家开始传,能获得最大总得分,求最大得分。1<=n<=1E5;0<=r[i]<n;1<=k<=1E10分析:与倍增法求l......