688马在棋盘上的概率

题目: 已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1),现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动,如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置.

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

法一: 自己的代码

思路: 写出状态转移方程,先记录每个格子在前N-1次不会跳出去的路径数,不断更新第N次的.

# 执行用时 :264 ms, 在所有 python3 提交中击败了37.50% 的用户
# 内存消耗 :12.8 MB, 在所有 python3 提交中击败了100.00%的用户
from typing import List
class Solution:
    def knightProbability(self, N: int, K: int, r: int, c: int) -> float:
        # 记录前K-1次的状态
        previous_status = [[1] * N for i in range(N)]
        # 记录八个方向
        dirc = [[1,2],[1,-2],
                [2,1],[2,-1],
                [-1,2],[-1,-2],
                [-2,1],[-2,-1]]
        # 进行K次移动
        for k in range(K):
            current_status = [[0] * N for i in range(N)]
            for i in range(N):
                for j in range(N):
                    for d in dirc:
                        p = d[0] + i
                        q = d[1] + j
                        # 如果超出边界了,则这次跳跃无效,
                        if p < 0 or q < 0 or p >= N or q >= N:
                            pass
                        else:
                            # 注意第一次的时候,previous_status为全1数组
                            # previous_status中记录的是前K-1次的状态,如第K-1次的时候,某个格子中的数是5,
                            # 表示第K-1次跳的时候,有5种方法跳到该格子
                            current_status[i][j] = current_status[i][j] + previous_status[p][q]
            # current_status中现在记录的是前K-1回(跳了8的K-1次)的跳跃中,有多少次在棋盘内
            previous_status = current_status
        return previous_status[r][c] / (8 ** K)
if __name__ == '__main__':
    duixiang = Solution()
    a = duixiang.knightProbability(10, 13, 5, 3)
    print(a)
    print(8 ** 13)
View Code

受576出界的路径数启发的回溯法代码,同576一样不用lru_cache的话会超时.注意回溯后哪些参数需要恢复到回溯前的状态.

# 执行用时 :200 ms, 在所有 python3 提交中击败了70.45% 的用户
# 内存消耗 :22.6 MB, 在所有 python3 提交中击败了8.00%的用户
from functools import lru_cache
from typing import List
class Solution:
    def knightProbability(self, N: int, K: int, r: int, c: int) -> float:
        # 记录前K-1次的状态
        previous_status = [[1] * N for i in range(N)]
        # 记录八个方向
        dirc = [[1,2],[1,-2],
                [2,1],[2,-1],
                [-1,2],[-1,-2],
                [-2,1],[-2,-1]]
        @lru_cache(None)
        def recursion(i,j,K):
            # 如果出界了返回0 或次数用完了
            if i < 0 or i >= N or j < 0 or j >= N or K < 0:
                return 0
            # 否则在界内,如果次数恰好用完了,说明是一条完整的路径,返回1
            elif K == 0:
                return 1
            # 否则继续遍历
            else:
                pass
            res = 0
            for p,q in dirc:
                # 下面这样写是错的,回溯函数调用结束的时候,无法返回原先的坐标值i,j
                # i = i + p
                # j = j + q
                K -= 1
                # 利用回溯函数的时候一定要注意哪些量在调用结束后,需要恢复成调用前的
                res += recursion(i+p,j+q,K)
                K += 1
            return res
        return recursion(i=r, j=c, K=K) / (8 ** K)
if __name__ == '__main__':
    duixiang = Solution()
    a = duixiang.knightProbability(10, 13, 5, 3)
    print(a)
    print(8 ** 13)
View Code

法二: 官方代码

转移矩阵

posted on 2019-12-28 18:50  吃我一枪  阅读(219)  评论(0编辑  收藏  举报

导航