骑士在棋盘上的概率[DFS经典优化方向->动态规划]

前言

常常DFS爆搜都是会超时的,即使有剪枝的加持,而超时的关键就是很多计算重复了。所以以空间换时间而闻名的动态规划刚好补上这个缺点,用空间把算过的都记下来,方便后面用,只需寻找一下前后递进关系即状态转移即可。

一、骑士在棋盘上的概率

在这里插入图片描述

二、DFS与动态规划

1、拥有Kn复杂度的DFS

// 骑士在棋盘上的概率。
// 超时。
public class KnightProbability {
    /*
    计算骑士走了k步,还在棋盘上的概率。
    8个方向当作8个树枝,走一步的成功概率1/8,相同层概率相加,不同层概率相乘。
     */
    public double knightProbability(int n, int k, int row, int column) {
        // 当不在棋盘上也不可走了。
        if (row < 0 || 0 > column || column >= n || n <= row) return 0d;
        // 当在棋盘上,但没有步数了,不用继续走了.
        if (k == 0) return 1;
        //开始往8个方向走。
        double rs = 0;
        // 左.
        rs += knightProbability(n, k - 1, row + 1, column - 2);
        rs += knightProbability(n, k - 1, row - 1, column - 2);
        // 右。
        rs += knightProbability(n, k - 1, row + 1, column + 2);
        rs += knightProbability(n, k - 1, row - 1, column + 2);
        // 上。
        rs += knightProbability(n, k - 1, row - 2, column - 1);
        rs += knightProbability(n, k - 1, row - 2, column + 1);
        // 下。
        rs += knightProbability(n, k - 1, row + 2, column - 1);
        rs += knightProbability(n, k - 1, row + 2, column + 1);
        // 返回到该层的成功概率积。
        return rs / 8;
    }

}

2、用空间减少重复计算的动态规划

// dfs超时,采用动规减少重复计算。
class KnightProbability2 {
    /*
    计算骑士走了k步,还在棋盘上的概率。
    走k步,落在棋盘上一格的概率(f[k][i][j]),是从走k-1步(f[k-1][?][?]),加最后从四面八方的格子过来这一步的概率之和。
     */
    static int[][] dirs = {{1, -2}, {-1, -2}, {1, 2}, {-1, 2}, {-2, -1}, {-2, 1}, {2, -1}, {2, 1}};

    public double knightProbability(int n, int k, int row, int column) {
        // 记录走k步,落在在棋盘上(i,j)的概率。
        double[][][] f = new double[k + 1][n][n];
        // 初始化:走0步,落在棋盘上(i,j)的概率为1.
        for (int i = 0; i < n; i++) Arrays.fill(f[0][i], 1);
        for (int step = 1; step <= k; step++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    // 更新状态 f[k][i][j],其为 (i,j) 的四面八方走 1 步过来的概率 * 1/8    的和。
                    // 8个方向
                    for (int[] dir : dirs) {
                        int ni = dir[0] + i, nj = dir[1] + j;
                        if (ni >= 0 && 0 <= nj && nj < n && n > ni)
                            f[step][i][j] += f[step - 1][ni][nj] / 8;
                    }
                }
            }
        }
        // 最终结果(逆向思维):
        // 棋盘之内的格子,走k步,落到(row,column)的概率。
        // 能k步走到(row,column)的,意味者从(row,column)反走k步,能都到棋盘之内的概率。
        return f[k][row][column];
    }

总结

1)DFS与动态规划,DFS通常用动态规划来解,用空间换时间。

参考文献

[1] LeetCode 骑士在棋盘上的概率

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值