您的位置:首页 > 其它

马拦过河卒

2018-03-31 13:04 99 查看
[问题描述]
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如下图中的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点(如下图中的C点和P1,P2,……,P8)。卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在从键盘输入n,m,要求计算出卒从A点能够到达B点的路径的条数。
 
 
[问题分析]
跳马是一道很老的题目,一些比赛中也经常出现过这一问题的变形(如NOIP1997初中组第三题)。有些同学一看到这种类型的题目就去盲目搜索,但事实证明:当n, m=15就会超时。
其实,本题稍加分析就能发现,要到达棋盘上的一个点,只能从左边过来(我们称之为左点)或是从上面过来(我们称之为上点),所以根据加法原理,到达某一点的路径数目,就等于到达其相邻的上点和左点的路径数目之和,因此我们可以使用逐列(或逐行)递推的方法来求出从起点到终点的路径数目。障碍点(马的控制点)也完全适用,只要将到达该点的路径数目设置为0即可。
假设用F[i,j]表示到达点(i,j)的路径数目,用g[i,j]表示点(i, j)是否是对方马的控制点,g[i,j]=0表示不是对方马的控制点,g[i,j]=1表示是对方马的控制点。则,我们可以得到如下的递推关系式:
F[i,j] = 0                     { g[i,j] = 1 }
F[i,0] = F[i-1,0]              {i > 0, g[i,j] = 0}
F[0,j] = F[0,j-1]              {j > 0, g[i,j] = 0}
F[i,j] = F[i-1,j] + F[i,j-1]   {i>0, j>0, g[i,j] = 0}
上述递推关系式的边界为:F[0,0] = 1。考虑到最大情况下:n=20,m=20,路径条数可能会超出长整数范围,所以要使用int64类型计数或高精度运算。
import java.util.Scanner;
public class 递归马拦过河卒 {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
// (n,m)代表终点
int n = s.nextInt();
int m = s.nextInt();
// (p,q)代表马的起始坐标
int p = s.nextInt();
int q = s.nextInt();
int g[][] = new int[n + 1][m + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if ((i == p && j == q)
|| (Math.abs(p - i) == 1 && Math.abs(q - j) == 2)
|| (Math.abs(p - i) == 2 && Math.abs(q - j) == 1)) {
g[i][j] = 1;
} else {
g[i][j] = 0;
}
}
}
System.out.println(f(n, m, g));
}

private static long f(int n, int m, int[][] g) {
if (g
[m] == 1)
return 0;
else if (n == 0 && m == 0)
return 1;
else if (n > 0 && m == 0 && g
[m] == 0)
return f(n - 1, 0, g);
else if (m > 0 && n == 0 && g
[m] == 0)
return f(0, m - 1, g);
else
return f(n - 1, m, g) + f(n, m - 1, g);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: