小韦老师@NOIP普及组-2002-过河卒
题目:
描述
如图,A 点有一个过河卒,需要走到目标 B 点。
卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的 C 点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。
例如上图 C 点上的马可以控制 9 个点(图中的 P1,P2 … P8 和 C)。卒不能通过对方马的控制点。
棋盘用坐标表示,A 点 (0,0)、B 点 (n,m) (n, m 为不超过 20 的整数,并由键盘输入),同样马的位置坐标是需要给出的(约定: C 不等于 A,同时 C 不等于 B)。现在要求你计算出卒从 A 点能够到达 B 点的路径的条数。
输入
B 点的坐标 (n, m) 以及对方马的坐标 (X, Y)。
输出
一个整数(路径的条数)。
输入样例1
6 6 3 2
输出样例1
17
题解:
思路
整体思路:
对于一个点来说,这个点只能往右走或者往下走,也就意味着一个点只能由它的左边的点或者上边的点走过来。
用 dp[i][j] 表示从点 (0, 0) 走到点 (i, j) 的路径条数,则等于点 (i, j) 的左边的点 (i, j-1) 的路径条数加上点 (i, j) 上边的点 (i-1, j) 的路径条数,也即:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
另外,由马控制的几个点不能走。
具体步骤:
- 定义 2 个数组,用数组 c 来存储棋盘,值为 -1 代表是马所能控制的点,不能走;数组 dp 用来存储到达每个点的路径条数。
const int N = 50;
int c[N][N];
long long dp[N][N];
- 定义方向数组,按照“马走日”的规则进行初始化:
int dir[8][2] = {{1, 2}, {2, 1}, {1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {-1, -2}, {-2, -1}};
-
定义 B 点(终点)的坐标 n 和 m,并输入 n 和 m。
-
定义马所在的坐标 x 和 y,并输入 x 和 y,并将棋盘中马的位置的值设为 -1,代表这个点不能走:
c[x][y] = -1;
- 枚举马能到的 8 个点,将这 8 个点的值设为 -1,代表这些点不能走
for (int i = 0; i < 8; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx >= 0 && tx <= n && ty >= 0 && ty <= m) { // 记得判断越界噢
c[tx][ty] = -1;
}
}
- 设置边界条件:
dp[0][0] = 1;
-
枚举棋盘的每一个位置,若当前位置的值不是 -1:
-
若 i > 0,则 dp[i][j] += dp[i-1][j];
-
若 j > 0,则 dp[i][j] += dp[i][j-1];
-
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (c[i][j] != -1) {
if (i) {
dp[i][j] = dp[i-1][j];
}
if (j) {
dp[i][j] += dp[i][j-1];
}
}
}
}
- 输出结果
cout << dp[n][m];
思考:
1° 这道题应该如何进行分解?你以后如何对于你分解到的知识学以致用?
2° 这道题跟数字三角形有何异同?请详细说明。
完整代码
#include <bits/stdc++.h>
using namespace std;
// 定义 2 个数组,用数组 c 来存储棋盘,值为 -1 代表是马所能控制的点,不能走;数组 dp 用来存储
// 到达每个点的路径条数
const int N = 50;
int c[N][N];
long long dp[N][N];
// 定义方向数组,按照“马走日”的规则进行初始化
int dir[8][2] = {{1, 2}, {2, 1}, {1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {-1, -2}, {-2, -1}};
int main() {
// 定义 B 点(终点)的坐标 n 和 m,并输入 n 和 m
int n, m;
cin >> n >> m;
// 定义马所在的坐标 x 和 y,并输入 x 和 y,并将棋盘中马的位置的值设为 -1,代表这个点不能走
int x, y;
cin >> x >> y;
c[x][y] = -1;
// 枚举马能到的 8 个点,将这 8 个点的值设为 -1,代表这些点不能走
for (int i = 0; i < 8; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx >= 0 && tx <= n && ty >= 0 && ty <= m) { // 记得判断越界噢
c[tx][ty] = -1;
}
}
// 设置边界条件
dp[0][0] = 1;
// 枚举棋盘的每一个位置,若当前位置的值不是 -1:
// 若 i > 0,则 dp[i][j] += dp[i-1][j];
// 若 j > 0,则 dp[i][j] += dp[i][j-1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (c[i][j] != -1) {
if (i) {
dp[i][j] = dp[i-1][j];
}
if (j) {
dp[i][j] += dp[i][j-1];
}
}
}
}
// 输出结果
cout << dp[n][m];
return 0;
}