【信息奥赛题解】过河卒(详细分析题解 & C++ 代码)

1314:【例3.6】过河卒(Noip2002)


时间限制: 1000 ms         内存限制: 65536 KB
提交数:45946    通过数: 20093

【题目描述】

棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。

【输入】

给出n、m和C点的坐标。

【输出】

从A点能够到达B点的路径的条数。

【输入样例】

8 6 0 4

【输出样例】

1617

【原题链接】信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)icon-default.png?t=N7T8https://ybt.ssoier.cn/problem_show.php?pid=1314

分析

有题目分析得出:以马为中心的八个点都是不能走的,以马为控制方,对于我们任何一个点来说,他可能的来源是从左边来,或者是从上边来,因为你从一个点出发,要么是往右去,要么就是往上去,由此可以映射出马的所有控制点,然后再判断马的控制点是否越界,卒只能向下或向右走,因此卒走到每个点都必须经过从a[i-1][j]点或者a[i][j-1]点,即a[i][j] = a[i-1][j] + a[i][j-1]。当遇到马的控制点时跳过。

#include <iostream>
using namespace std;

//定义了两个二维数组 a 和 b,a 用来存储走到每个点(i, j) 的路径条数,b 用来标记马脚的位置。
long long a[25][25];//a[i][j]为走到i,j这一点的路径条数
bool b[25][25];//用来标记是否是马脚位置
//八个马脚位置的相对位置
//dx 和 dy 分别定义了八个可能的马走日的相对位移。
int dx[8] = { 1, 1, 2, 2, -1, -1, -2, -2 };   
int dy[8] = { 2, -2, 1, -1, 2, -2, 1, -1 };
int m, n, cx, cy;  //定义了四个整型变量 m, n, cx, cy,分别用来存储输入的行数、列数、起始位置的行坐标和列坐标。
int main() {
    cin >> m >> n >> cx >> cy;   //从标准输入读取 m, n, cx, cy 的值,分别表示行数、列数以及起始位置的行列坐标。
    //m++;n++;cx++;cy++;
    b[cx][cy] = 1;  //将起始位置 (cx, cy) 标记为马脚位置。

    //遍历八个可能的马走日的相对位置,计算出每个可能位置 (xx, yy),并将其标记为马脚位置,条件是该位置在合法的棋盘范围内。
    for (int i = 0; i < 8; i++) {
        int xx = cx + dx[i];
        int yy = cy + dy[i];
        if (xx >= 0 && xx <= m && yy >= 0 && yy <= n) {
            b[xx][yy] = 1; //标记马脚的位置
        }
    }
    //使用动态规划计算从起始位置 (0, 0) 到每个位置 (i, j) 的路径条数 a[i][j]:

    //如果(i, j) 是马脚位置,则 a[i][j] 设为 0。
    //如果(i, j) 是起步位置(0, 0),则 a[i][j] 设为 1。
    //否则,a[i][j] 等于从上方(i - 1, j) 和左边(i, j - 1) 过来的路径条数之和。
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (b[i][j] == 1) { //如果是马脚,跳过
                a[i][j] = 0;//路径条数为0
                continue;
            }
            if (i == 0 && j == 0) {//起步位置路径条数为1
                a[i][j] = 1;
            }
            else {
                if (i != 0) {//上边有路
                    a[i][j] += a[i - 1][j];//加上边过来的路径数
                }
                if (j != 0) {//左边有路
                    a[i][j] += a[i][j - 1];//加左边过来的路径数
                }
            }
        }
    }
    cout << a[m][n];  //输出从 (0, 0) 到 (m, n) 的路径条数。
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值