688. Knight Probability in Chessboard

原题链接:https://leetcode.com/problems/knight-probability-in-chessboard/description/
在看完《数据结构(C语言版)》中的“树”章节里面的回溯法后,就找了些相关问题来练习。回溯法相关的问题有:八皇后问题、骑士游历问题、迷宫问题和选择最优解问题,这道题目正是骑士游历问题的变种。

我的思路

看完题目后,首先我写下了如此实现:

/**
 * Created by clearbug on 2018/2/26.
 */
public class Solution {

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.knightProbability(8, 10, 6, 4));
    }

    /**
     * 骑士走过 K 步后仍在棋盘上的概率
     *
     * @param N 棋盘的大小
     * @param K 总步数
     * @param r 起始纵坐标
     * @param c 起始横坐标
     * @return 概率
     */
    public double knightProbability(int N, int K, int r, int c) {
        return dfs(N, K, r, c, 0, 1);
    }

    public double dfs(int N, int K, int r, int c, int stepCount, double currProbability) {

        // System.out.println("N = " + N + ", K = " + K + ", r = " + r + ", c = " + c + ", stepCount = " + stepCount + ", currProbability = " + currProbability);
        if (stepCount == K) {
            return currProbability;
        }

        double res = 0.0;

        if (r - 2 >= 0) {
            if (c - 1 >= 0) {
                res += dfs(N, K, r - 2, c - 1, stepCount + 1, currProbability * 0.125);
            }
            if (c + 1 < N) {
                res += dfs(N, K, r - 2, c + 1, stepCount + 1, currProbability * 0.125);
            }
        }
        if (r + 2 < N) {
            if (c - 1 >= 0) {
                res += dfs(N, K, r + 2, c - 1, stepCount + 1, currProbability * 0.125);
            }
            if (c + 1 < N) {
                res += dfs(N, K, r + 2, c + 1, stepCount + 1, currProbability * 0.125);
            }
        }

        if (r - 1 >= 0) {
            if (c - 2 >= 0) {
                res += dfs(N, K, r - 1, c - 2, stepCount + 1, currProbability * 0.125);
            }
            if (c + 2 < N) {
                res += dfs(N, K, r - 1, c + 2, stepCount + 1, currProbability * 0.125);
            }
        }
        if (r + 1 < N) {
            if (c - 2 >= 0) {
                res += dfs(N, K, r + 1, c - 2, stepCount + 1, currProbability * 0.125);
            }
            if (c + 2 < N) {
                res += dfs(N, K, r + 1, c + 2, stepCount + 1, currProbability * 0.125);
            }
        }

        return res;
    }


}

代码简单易懂,我还以为自己能终于独立做出一道中等难度的题目了呢?没想到还是 too naive,运行到这组测试用例 [8, 30, 6, 4] 时,运行超时了。。。然后我把这组测试用例放到自己的 IDEA 中跑了下确实运行了五分钟左右才运行完毕。看来得优化程序了,看了半天不知怎么优化。。。还是去看官方解答吧!

官方答案一

这个答案是根据一个递推公式来计算的,运行速度比起我的实现来确实快了很多,几十个测试用例十几毫秒运行完成。现在也没想明白我的方法究竟慢在哪里??

/**
 * Created by clearbug on 2018/2/26.
 */
public class Solution {

    public static void main(String[] args) {
        Solution s = new Solution();
        System.out.println(s.knightProbability(8, 10, 6, 4));
    }

    /**
     * 骑士走过 K 步后仍在棋盘上的概率
     *
     * @param N 棋盘的大小
     * @param K 总步数
     * @param r 起始纵坐标
     * @param c 起始横坐标
     * @return 概率
     */
    public double knightProbability(int N, int K, int r, int c) {
        double[][] dp = new double[N][N];
        int[] dr = new int[]{2, 2, 1, 1, -1, -1, -2, -2};
        int[] dc = new int[]{1, -1, 2, -2, 2, -2, 1, -1};

        dp[r][c] = 1;
        for (; K > 0; K--) {
            double[][] dp2 = new double[N][N];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    for (int k = 0; k < 8; k++) {
                        int cr = i + dr[k];
                        int cc = j + dc[k];
                        if (cr >= 0 && cr < N && cc >= 0 && cc < N) {
                            dp2[cr][cc] += dp[i][j] / 8.0;
                        }
                    }
                }
            }
            dp = dp2;
        }
        double res = 0.0;
        for (double[] row : dp) {
            for (double x : row) {
                res += x;
            }
        }
        return res;
    }

}

官方方法二

官方的方法二有点复杂,自己英语也不大好,竟然没看懂。。。好像就是利用矩阵相乘以及求幂等操作实现的,以后有时间了再细看吧。。。

posted @ 2018-03-08 11:12  optor  阅读(272)  评论(0编辑  收藏  举报