P1002 [NOIP 2002 普及组] 过河卒——动态规划
题目描述
棋盘上 \(A\) 点有一个过河卒,需要走到目标 \(B\) 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 \(C\) 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,\(A\) 点 \((0, 0)\)、\(B\) 点 \((n, m)\),同样马的位置坐标是需要给出的。

现在要求你计算出卒从 \(A\) 点能够到达 \(B\) 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 \(B\) 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
输入输出样例 #1
输入 #1
6 6 3 3
输出 #1
6
说明/提示
对于 \(100 \%\) 的数据,\(1 \le n, m \le 20\),\(0 \le\) 马的坐标 \(\le 20\)。
【题目来源】
NOIP 2002 普及组第四题
题解
这是一道典型的动态规划问题,关键在于利用动态规划的思想,通过逐步计算从起点到每个点的路径数,并考虑马的控制点不能通过。
解析
- 状态定义:
- 设
dp[i][j]表示从A(0, 0)点到(i, j)点的路径条数。 - 初始状态:
dp[0][0] = 1,因为起点本身有一条路径(即不移动)。
- 设
- 状态转移方程:
- 对于非马控制点的点
(i, j),它可以从上方(i - 1, j)或左方(i, j - 1)到达。 - 所以
dp[i][j] = dp[i - 1][j] + dp[i][j - 1](当i > 0且j > 0时)。 - 当
i == 0 && j > 0时,dp[i][j] = dp[i][j - 1],即只能从左方到达。 - 当
j == 0 && i > 0时,dp[i][j] = dp[i - 1][j],即只能从上方到达。
- 对于非马控制点的点
- 马控制点处理:
- 马的控制点及其自身设为不可达,即
dp[i][j] = 0。 - 马能控制的点包括马自身以及马按 “日” 字走法能到达的点。例如马在
(x, y),则马能控制的点有(x + 1, y + 2), (x + 2, y + 1), (x - 1, y + 2), (x - 2, y + 1), (x + 1, y - 2), (x + 2, y - 1), (x - 1, y - 2), (x - 2, y - 1), (x, y)。
- 马的控制点及其自身设为不可达,即
代码实现(Python)
n, m, x, y = map(int, input().split())
dp = [[0] * (m + 1) for _ in range(n + 1)]
# 标记马的控制点
horse_control = [(x, y), (x + 1, y + 2), (x + 2, y + 1), (x - 1, y + 2), (x - 2, y + 1), (x + 1, y - 2), (x + 2, y - 1), (x - 1, y - 2), (x - 2, y - 1)]
for i, j in horse_control:
if 0 <= i <= n and 0 <= j <= m:
dp[i][j] = -1
# 初始化起点
dp[0][0] = 1 if dp[0][0]!= -1 else 0
for i in range(n + 1):
for j in range(m + 1):
if dp[i][j] == -1:
continue
if i > 0 and dp[i - 1][j]!= -1:
dp[i][j] += dp[i - 1][j]
if j > 0 and dp[i][j - 1]!= -1:
dp[i][j] += dp[i][j - 1]
print(dp[n][m])
代码实现(C++)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25;
long long dp[N][N];
bool horse_control[N][N];
int main() {
int n, m, x, y;
cin >> n >> m >> x >> y;
memset(horse_control, false, sizeof(horse_control));
horse_control[x][y] = true;
int dx[] = {1, 2, -1, -2, 1, 2, -1, -2};
int dy[] = {2, 1, 2, 1, -2, -1, -2, -1};
for (int i = 0; i < 8; ++i) {
int new_x = x + dx[i];
int new_y = y + dy[i];
if (new_x >= 0 && new_x <= n && new_y >= 0 && new_y <= m) {
horse_control[new_x][new_y] = true;
}
}
dp[0][0] = horse_control[0][0]? 0 : 1;
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
if (horse_control[i][j]) {
continue;
}
if (i > 0 &&!horse_control[i - 1][j]) {
dp[i][j] += dp[i - 1][j];
}
if (j > 0 &&!horse_control[i][j - 1]) {
dp[i][j] += dp[i][j - 1];
}
}
}
cout << dp[n][m] << endl;
return 0;
}
最终答案即为 dp[n][m],它表示从 A 点到 B 点的路径条数。
浙公网安备 33010602011771号