洛谷 - P1002 [NOIP 2002 普及组] 过河卒 - 解题思路

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 ≤ n, m ≤ 20,0 ≤ 马的坐标 ≤ 20

【题目来源】

NOIP 2002 普及组第四题

题目描述

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。

  • A 点 (0, 0)、B 点 (n, m)
  • 输入:B 点坐标和马的坐标
  • 输出:卒从 A 点到达 B 点的路径条数
  • 数据范围:1 ≤ n, m ≤ 20,0 ≤ 马的坐标 ≤ 20

问题简化

想象你在一个网格上,从左上角 (0,0) 走到右下角 (n,m)。你只能往下走或往右走,但有些格子被"马"控制了,不能踩。问:有多少种走法?

第一步:搞清楚哪些格子不能走

马在中国象棋里走"日"字,从马的位置可以跳到8个点。所以马控制的格子 = 马自己的位置 + 8个能跳到的位置。

马能跳到的8个方向(相对马的位置):
(-2,1), (-1,2), (1,2), (2,1), (2,-1), (1,-2), (-1,-2), (-2,-1)

把这些点全部标记为"禁区"。

第二步:动态规划的核心思想

问自己一个问题:到达格子 (i, j) 有多少种走法?

因为卒只能向下或向右走,所以到达 (i, j) 只有两种可能:

  • 从上面 (i-1, j) 走下来
  • 从左边 (i, j-1) 走过来

所以:

dp[i][j] = dp[i-1][j] + dp[i][j-1]

意思是:到达当前格子的路径数 = 到达上方格子的路径数 + 到达左边格子的路径数

第三步:处理特殊情况

  • 起点 dp[0][0] = 1(只有一种方式:就站在那)
  • 如果某个格子是禁区,dp[i][j] = 0(走不了,路径数为0)
  • 如果起点就是禁区,直接输出0

举个例子

假设没有马,3x3的格子:

1 → 1 → 1
↓   ↓   ↓
1 → 2 → 3
↓   ↓   ↓
1 → 3 → 6

每个格子的数字就是到达它的路径数。右下角是6,说明有6种走法。

如果中间有个禁区,那个格子变成0,后面的格子就会少很多路径。

总结

  1. 标记马的控制点为禁区
  2. 从左上角开始,逐格计算路径数
  3. 禁区路径数为0,其他格子 = 上方 + 左方
  4. 最后 dp[n][m] 就是答案

完整代码

n, m, hx, hy = map(int, input().split())

# 马的8个跳跃方向
horse_moves = [(-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1)]

# 标记马的控制点
blocked = set()
blocked.add((hx, hy))
for dx, dy in horse_moves:
    nx, ny = hx + dx, hy + dy
    if 0 <= nx <= n and 0 <= ny <= m:
        blocked.add((nx, ny))

# DP
dp = [[0] * (m + 1) for _ in range(n + 1)]
dp[0][0] = 0 if (0, 0) in blocked else 1

for i in range(n + 1):
    for j in range(m + 1):
        if (i, j) in blocked:
            dp[i][j] = 0
        else:
            if i > 0:
                dp[i][j] += dp[i - 1][j]
            if j > 0:
                dp[i][j] += dp[i][j - 1]

print(dp[n][m])