[NOIP2002 普及组] 过河卒
题目描述
棋盘上 A A A 点有一个过河卒,需要走到目标 B B B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示, A A A 点 ( 0 , 0 ) (0, 0) (0,0)、 B B B 点 ( n , m ) (n, m) (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A A A 点能够到达 B B B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B B B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
样例 #1
样例输入 #1
6 6 3 3
样例输出 #1
6
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ n , m ≤ 20 1 \le n, m \le 20 1≤n,m≤20, 0 ≤ 0 \le 0≤ 马的坐标 ≤ 20 \le 20 ≤20。
【题目来源】
NOIP 2002 普及组第四题
解题思路
这个题主要的任务就是找到递推式:
由于兵只能向右和向下移动,所以每一个点的路径数等于它上面一个点和它左边一个点的路径数之和
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
{f[i][j]=f[i-1][j]+f[i][j-1]}
f[i][j]=f[i−1][j]+f[i][j−1]
初始条件
为了在写递推循环时不需要考虑数组小标越界问题,我们可以把以0为行数或者列数的单元格的路线数预计算出来,这样在循环时可以直接从1开始数组下标恒大于0不会越界!
- 首行的每个单元格若不受马的控制点影响,初始化为1;若位于马的控制点,赋值为0,同时后面的所有点都不可能到达,所以赋值为0
- 列的情况同理
f [ 0 ] [ j ] = { 1 , 不是马的控制点 0 , 是马的控制点及马的控制点以右的点 f[0][j]=\begin{cases} 1,& \text{不是马的控制点}\\ 0,& \text{是马的控制点及马的控制点以右的点} \end{cases} f[0][j]={1,0,不是马的控制点是马的控制点及马的控制点以右的点
f [ i ] [ 0 ] = { 1 , 不是马的控制点 0 , 是马的控制点及马的控制点以下的点 f[i][0]=\begin{cases} 1,& \text{不是马的控制点}\\ 0,& \text{是马的控制点及马的控制点以下的点} \end{cases} f[i][0]={1,0,不是马的控制点是马的控制点及马的控制点以下的点
限制条件
我们需要计算出马的控制点,可以预先打出一张马跳动范围的横纵坐标表,然后循环判断是否满足条件后在标记数组中给该点打上标记,具体实现可以看我下面的代码
注意:不要忘了给马的点自己打上标记
参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[21][21]; //记录结果的数组,开long long
bool fb[21][21]; //判断是否是马的控制点的数组
int dx[9] = {0, -1, -1, -2, -2, 1, 1, 2, 2};
int dy[9] = {0, 2, -2, 1, -1, 2, -2, 1, -1};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int a, b, n, m;
cin >> a >> b >> n >> m;
for (int i = 0; i <= 8; i++) //标记马的控制点
{
int nx = n + dx[i], ny = m + dy[i];
if (nx < 0 || ny < 0 || nx > 20 || ny > 20)
continue;
// cout << "controlled: " << nx << " " << ny << endl;
fb[nx][ny] = true;
}
for (int i = 1; i <= a; i++)
{
if (!fb[i][0])
f[i][0] = 1;
else
break;
}
for (int i = 1; i <= b; i++)
{
if (!fb[0][i])
f[0][i] = 1;
else
break;
}
for (int i = 1; i <= a; i++)
{
for (int j = 1; j <= b; j++)
{
if (!fb[i][j])
f[i][j] = f[i - 1][j] + f[i][j - 1];
// cout << i << " " << j << " " << f[i][j] << endl;
}
}
cout << f[a][b];
return 0;
}
``