小韦老师@NOIP普及组-2002-过河卒

小韦老师@NOIP普及组-2002-过河卒

题目:

描述

如图,A 点有一个过河卒,需要走到目标 B 点。

卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的 C 点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。

例如上图 C 点上的马可以控制 9 个点(图中的 P1,P2 … P8 和 C)。卒不能通过对方马的控制点。

xxx

棋盘用坐标表示,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] 

另外,由马控制的几个点不能走。

具体步骤:
  1. 定义 2 个数组,用数组 c 来存储棋盘,值为 -1 代表是马所能控制的点,不能走;数组 dp 用来存储到达每个点的路径条数。
    const int N = 50;
    int c[N][N];
    long long dp[N][N];
  1. 定义方向数组,按照“马走日”的规则进行初始化:
	int dir[8][2] = {{1, 2}, {2, 1}, {1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {-1, -2}, {-2, -1}};
  1. 定义 B 点(终点)的坐标 n 和 m,并输入 n 和 m。

  2. 定义马所在的坐标 x 和 y,并输入 x 和 y,并将棋盘中马的位置的值设为 -1,代表这个点不能走:

	c[x][y] = -1;
  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;
        }
    }
  1. 设置边界条件:
	dp[0][0] = 1;
  1. 枚举棋盘的每一个位置,若当前位置的值不是 -1:

    1. 若 i > 0,则 dp[i][j] += dp[i-1][j];

    2. 若 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];
            	}
            }
        }
    }
  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;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值