问题描述
给你一个棋盘,长为10,宽为9,然后在棋盘的[0,0]位置,有一个马,马走日嘛,就是象棋的规则,现在在棋盘上给你一个位置[a,b],给你k步,请你求出,马走到【a,b】位置并且走k步,可以有多少种走法?
暴力递归
先说一下总体思路,马走k步从【0,0】位置走到【a,b】,其实和从ab走到0是不是一样啊?因此,思路中虽然是0到ab,但是在代码中,写ab到0比较容易一些。
递归出口base case
当马走出棋盘时,说明失败了,返回0。
当k步走完时,如果a=0且b=0,说明找到一种,返回1,否则返回0。
递归函数
当base case不成立时,就向周围八个方向走日,然后将结果加起来返回。
代码
public static int f1(int x,int y,int k){
if(x<0||x>8||y<0||y>9){
return 0;
}
if(k==0){
return (x==0&&y==0)? 1:0;
}
int res=f1(x+1,y+2,k-1);
res+=f1(x+1,y-2,k-1);
res+=f1(x+2,y+1,k-1);
res+=f1(x+2,y-1,k-1);
res+=f1(x-1,y+2,k-1);
res+=f1(x-1,y-2,k-1);
res+=f1(x-2,y+1,k-1);
res+=f1(x-2,y-1,k-1);
return res;
}
三维动态规划
因为此题的递归解法中,有三个可变参数,因此,画图的结果是三维图,也就是立方体。三维的动态规划,其实本质上和二维是一样的。
public static int dpWays(int x,int y,int k){
//排除掉特殊情况
if(x<0||x>8||y<0||y>9||k<0){
return 0;
}
//创建三维表格
int[][][] dp=new int[9][10][k+1];
//根据递归出口写出第一层的结果,只有【000】位置是1,其余都是0,数组默认值是0,所以不用写了
dp[0][0][0]=1;
//h从1开始,因为第一层已经已知结果了
for (int h = 1; h <=k; h++) {
//i、j都之和上一层有关,因此,ij的顺序无所谓
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
//根据递归函数得出:上面一层的dp结果是由下一层的八个位置相加得出的
dp[i][j][h]+=getValue(dp,i-1,j+2,h-1);
dp[i][j][h]+=getValue(dp,i-1,j-2,h-1);
dp[i][j][h]+=getValue(dp,i+1,j+2,h-1);
dp[i][j][h]+=getValue(dp,i+1,j-2,h-1);
dp[i][j][h]+=getValue(dp,i+2,j+1,h-1);
dp[i][j][h]+=getValue(dp,i+2,j-1,h-1);
dp[i][j][h]+=getValue(dp,i-2,j+1,h-1);
dp[i][j][h]+=getValue(dp,i-2,j-1,h-1);
}
}
}
return dp[x][y][k];
}
//处理越界问题
private static int getValue(int[][][] dp, int x, int y, int k) {
if(x<0||x>8||y<0||y>9||k<0){
return 0;
}
return dp[x][y][k];
}