原题链接: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;
}
}
官方方法二
官方的方法二有点复杂,自己英语也不大好,竟然没看懂。。。好像就是利用矩阵相乘以及求幂等操作实现的,以后有时间了再细看吧。。。