象棋中马走日,从(0,0)点出发到达目标位置,规定走K步,问:能走到目标位置的所有方法数,象棋棋盘规模10*9大小。
public static int jump(int a, int b, int K) {
return process(0, 0, K, a, b);
}
方法中的参数解释:
1.int a和 int b,代表目标位置是(a,b)
2.int K,代表总共走K步
开始暴力递归:
假设当前位置在(x,y)处(任一位置),已经走了一部分,还剩下rest步需要走,目标位置是(a,b),那么在当前位置处一共八种可能性:(x+1,y+2)、(x+2,y+1)、(x+2,y-1)、(x+1,y-2)、(x-1,y-2)、(x-2,y-1)、(x-2,y+1)、(x-1,y+2),可以画一个坐标轴方便理解一点。
考虑边界:当前位置(x,y)的八种可能性,如果跳出棋盘喃?那么则返回0种方法。
递归退出条件:当剩余步数为0,并且刚好到达目标位置,退出递归,或者边界越界。
/**
* 当前位置(x,y);目标位置(a,b);还有rest步需要走
* 棋盘规模:10*9
*
* @return 返回走完rest步到达目标位置的方法数
*/
public static int process(int x, int y, int rest, int a, int b) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
if (rest == 0) {
return (x == a && y == b) ? 1 : 0;
}
int p1 = process(x + 1, y + 2, rest - 1, a, b);
int p2 = process(x + 2, y + 1, rest - 1, a, b);
int p3 = process(x + 2, y - 1, rest - 1, a, b);
int p4 = process(x + 1, y - 2, rest - 1, a, b);
int p5 = process(x - 1, y - 2, rest - 1, a, b);
int p6 = process(x - 2, y - 1, rest - 1, a, b);
int p7 = process(x - 2, y + 1, rest - 1, a, b);
int p8 = process(x - 1, y + 2, rest - 1, a, b);
return p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
}
由分析可知,递归方法中,影响步数的是x、y、剩余步数rest这三个,显而易见是一个三维dp数组。当rest==0的时候,刚好到目标位置时,方法数是1,其余时候全是0。那么先确定rest==0的时候(也就是第一层)dp数组的值。然后会发现每一层的rest都是依赖下一层(rest-1)的dp数组的值,那么可以从下到上完成dp数组。要注意判断(x,y)跳到下一步之后是否越界。所以先写一个判断是否越界的方法,如果从当前位置跳完之后越界,那么返回0,不越界的话从dp数组中找到对应的值返回。
代码如下:
//判断是否越界的方法
public static int pick(int[][][] dp, int x, int y, int rest) {
if (x < 0 || x > 9 || y < 0 || y > 8) {
return 0;
}
return dp[x][y][rest];
}
有了这个方法之后完成dp数组:
public static int jumpDP(int a, int b, int K) {
int[][][] dp = new int[10][9][K + 1];
dp[a][b][0] = 1;
for (int rest = 1; rest < K + 1; rest++) {//rest从倒数第二层开始写
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 9; y++) {
int p1 = pick(dp, x + 1, y + 2, rest - 1);
int p2 = pick(dp, x + 2, y + 1, rest - 1);
int p3 = pick(dp, x + 2, y - 1, rest - 1);
int p4 = pick(dp, x + 1, y - 2, rest - 1);
int p5 = pick(dp, x - 1, y - 2, rest - 1);
int p6 = pick(dp, x - 2, y - 1, rest - 1);
int p7 = pick(dp, x - 2, y + 1, rest - 1);
int p8 = pick(dp, x - 1, y + 2, rest - 1);
dp[x][y][rest] = p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
}
}
}
return dp[0][0][K];
}