题面
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点 (0, 0)、B 点 (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
解析
起初我直接用递归的方法直接一步一步的追溯, 但由于递归本身很耗时间, 因此超时.
于是我开始想怎样可以更快的找到 A (0, 0) 到 B 点的所有路径,.
首先, 我们知道卒的走法只有向下和向右, 那么先假设没有马的存在, 从 A(0, 0) 点开始走, 到 (0, 1) 点的路径条数为 1; 到 (1, 0) 点的路径条数也为 1; 那么 到 (1, 1) 的路径有几条呢?
我们知道在到 (1, 1) 的前一步, 卒一定在 (0, 1) 和 (1, 0) 中的其中一个位置, 也就说从 (0, 0) 到 (1, 1) 的路径条数就是到 (0, 1) 和到 (1, 0) 的路径条数之和, 即到 (1,1) 点的路径有 2 条.
由此我们可以继续推出卒到 (n, m) 点的路径条数的递推式为: d[n][m] = d[n - 1][m] +d[n][m - 1].
由此我们可以用 d[i][j] 来储存从 A(0, 0) 点到该点的路径条数.
例子
Input
6 6 3 3
Output
6
图形解析
以下是我写的代码:
#include <iostream>
#include <algorithm>
using namespace std;
long long ma[30][30], n, m, x, y;//数据结果可能超过 int;
int b[9][2] = {{0, 0}, {-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {2, 1}, {1, 2}, {2, -1}, {1, -2}};
//马控制点相对于马所在位置的距离;
int main() {
cin >> n >> m >> x >> y;
x += 1;//加 1 是为了留出一个边界以使 ma[i - 1][j] 和 ma[i][j - 1] 都在数组范围内;
y += 1;//此时 A 点对应数组 ma[1][1];
n += 1;
m += 1;
ma[1][1] = 1;
for (int i = 0; i < 9; i++) {
ma[x + b[i][0]][y + b[i][1]] = -1;//标记马的控制点;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (ma[i][j] != -1) {//当该点不是马的控制点时, 进行一下操作;
if (ma[i - 1][j] == -1 || ma[i][j - 1] == -1) {
//该判断语句是看该点上和左是否是马的控制点;
//注意卒的左和上最多只有一个是马的控制点, 因此一次性进行判断;
ma[i][j] += 1;//若马的上和下有马的控制点, 则加 1 以抵消下式加上的负一;
}
ma[i][j] += ma[i - 1][j] + ma[i][j - 1];//路径条数的递推公式;
}
}
}
cout << ma[n][m] << endl;
return 0;
}