688. 马在棋盘上的概率(Python)

2020-12-01  本文已影响0人  玖月晴
  1. 马在棋盘上的概率(Python)

题目

难度:★★★☆☆
类型:数组
方法:动态规划

力扣链接请移步本题传送门
更多力扣中等题的解决方案请移步力扣中等题目录

已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。

现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。

国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。

现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。

求移动结束后,“马” 仍留在棋盘上的概率。

示例:

输入: 3, 2, 0, 0
输出: 0.0625
解释:
输入的数据依次为 N, K, r, c
第 1 步时,有且只有 2 种走法令 “马” 可以留在棋盘上(跳到(1,2)或(2,1))。对于以上的两种情况,各自在第2步均有且只有2种走法令 “马” 仍然留在棋盘上。
所以 “马” 在结束后仍在棋盘上的概率为 0.0625。

注意:

N 的取值范围为 [1, 25]
K 的取值范围为 [0, 100]
开始时,“马” 总是位于棋盘上

解答

方法1:动态规划

【矩阵定义】:定义一个K+1xNxN维度的三维矩阵dp,其中任意一点(k_, r_, c_)表示马走k_步后落在点(r_, c_)处的概率。这里在K维度上增加一维用来表示初始状态也就是马走零步的状态。

【初始状态】
由于马的最初位置是(r,c),因此马走零步落在点(r,c)的概率为1,因此填充dp[0][r][c] = 1,其他位置填充零。

【递推公式】
对于当前位置(r_, c_),马可以由八个方向跳过来:directions= [(2, 1), (2, -1), (1, 2), (1, -2), (-2, 1), (-2, -1), (-1, 2), (-1, -2)],对于每一个位置,马也可以等概率的跳到以上八个方向中任意一个。每一步的概率取决于上一步,每一个点的概率取决于八个特定方向临近点,因此,递推公式为:

dp[k_][r_][c_] = sum([dp[k_-1][r_+dr][c_+dc]/8 for dr, dc in directions])

这里需要注意判断求解概率的点不要越过棋盘边界。

【最终结果】

最后,我们选择最后一步的概率网格,求取网格所有点的和,即为K步后马还在棋盘中的概率。

class Solution:
    def knightProbability(self, N, K, r, c):
        dp = [[[0 for _ in range(N)] for _ in range(N)] for _ in range(K+1)]
        dp[0][r][c] = 1
        directions = [(2, 1), (2, -1), (1, 2), (1, -2),
                      (-2, 1), (-2, -1), (-1, 2), (-1, -2)
                      ]

        for k_ in range(1, K+1):
            print(dp[k_])
            for r_ in range(N):
                for c_ in range(N):
                    for dr, dc in directions:
                        if 0 <= r_+dr < N and 0 <= c_+dc < N:
                            dp[k_][r_][c_] += dp[k_-1][r_+dr][c_+dc] / 8
        return sum([prob for row in dp[-1] for prob in row])

方法2:记忆化深度优先搜索

为了简化计算,我们使用字典记录已经出现过的概率值,即将上述的三维网格按照位置-概率进行对应,通过及时的查找避免重复的计算。

这里用深度优先搜索方式代替动态规划,其一般规则是:首先判断特殊情况,例如越界或记录已经存在于字典中。注意这里dfs函数的返回值代表的是以从点(r,c)出发,K步后马还在棋盘上的概率,而非落在(r,c)点的概率。

class Solution:
    def knightProbability(self, N, K, r, c):

        memory = dict()
        directions = ((-1, -2), (-2, -1), (-2, 1), (-1, 2), (1, -2), (2, -1), (2, 1), (1, 2))

        def dfs(K, r, c):
            if not (0 <= r < N and 0 <= c < N):
                return 0
            if K == 0:
                return 1
            if (K, r, c) in memory:
                return memory[(K, r, c)]
            p = sum([dfs(K - 1, r + dr, c + dc) / 8.0 for dr, dc in directions])
            memory.update({(K, r, c): p})
            return p
        return dfs(K, r, c)

如有疑问或建议,欢迎评论区留言~

有关更多力扣中等题的python解决方案,请移步力扣中等题解析

上一篇下一篇

猜你喜欢

热点阅读