【NC16708】过河卒

题目

过河卒

动态规划

思路

我们知道,小卒只能朝右或者下走,先假设没有对方的马。那么对于起点所在的行上的所有位置就只有一种不同路径,因为那些位置只能从左边到达(转换思维),同样那么对于起点所在的列上的所有位置也只有一种不同路径,因为那些位置只能从上边到达。所以,我们可以得到以下思路:

建立一个 n n n m m m 列的二维数组 d p dp dp,其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示到达第 i i i 行第 j j j 列的不同路径种数,可知题目所求的答案就是 d p [ n ] [ m ] dp[n][m] dp[n][m]
对于 d p dp dp 数组的第一列和第一行(下标为 0 0 0),我们将其初始化为 1 1 1,然后从下标为 ( 1 , 1 ) (1,1) (1,1) 的格子开始依次遍历后续格子,根据上面的推导可以知道 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j]=dp[i-1][j]+dp[i][j-1] dp[i][j]=dp[i1][j]+dp[i][j1]

上面的思路针对没有对方的马的情况,但是这道题存在对方的一匹马,那又该怎么办呢?

我们知道,小卒不能通过对方马的控制点,也就是说,如果从左上角到右下角只要遇到控制点,那么控制点之右和控制点之下的位置就都不能到达了。而控制点本身对不同路径数目的贡献为 0 0 0,所以可以在 d p dp dp 数组中将所有的控制点都置为 0 0 0。这样,为了不让 d p dp dp 数组内的值产生混淆,即不知道该更新哪些点(因为控制点是不能更新的),所以在初始化的时候就先将整个 d p dp dp 数组全部置为 − 1 -1 1,然后将所有控制点都置为 0 0 0,然后更新的时候只更新值为 − 1 -1 1 的格子即可。

代码

#include <stdio.h>

// 马所能到达的几个点的位置变化
const int TO[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2},
                     {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};

int main(void) {
    int n = 0, m = 0, x = 0, y = 0;
    scanf("%d%d%d%d", &n, &m, &x, &y);
    // 需要使用长整型存储
    long long dp[n + 1][m + 1];
    int i = 0, j = 0, tx = 0, ty = 0;
    // 初始时全部置为-1
    for (i = 0; i <= n; i++) {
        for (j = 0; j <= m; j++) {
            dp[i][j] = -1;
        }
    }
    // 然后将马能到达的点全部置为0
    dp[x][y] = 0;
    for (i = 0; i < 8; i++) {
        tx = x + TO[i][0];
        ty = y + TO[i][1];
        if (!(tx < 0 || tx > n || ty < 0 || ty > m)) {
            dp[tx][ty] = 0;
        }
    }
    // 对于第一行,从起点到第一个为0的点,全部置为1,意思为
    // 从起点走到该位置只有一种路径
    for (i = 0; i <= m; i++) {
        if (dp[0][i] == -1) {
            dp[0][i] = 1;
        } else {
            for (i++; i <= m; i++) {
                dp[0][i] = 0;
            }
        }
    }
    // 同样,对于第一列(除起点外)也采用同样的方式处理为 1
    for (i = 1; i <= n; i++) {
        if (dp[i][0] == -1) {
            dp[i][0] = 1;
        } else {
            for (i++; i <= n; i++) {
                dp[i][0] = 0;
            }
        }
    }
    // 从第2行(下标为1)第2列(下标为1)
    // 开始遍历,凡是空位(值为-1)的格子都将其置为
    // 上边格子的数 + 左边格子的数
    // 这是因为到某个格子只能从上面和左边到这个格子,那么到达这个格子的路径数目
    // 就等于到达上面格子的路径数目 + 到达左边格子的路径数目
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= m; j++) {
            if (dp[i][j] == -1) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    }
    printf("%lld\n", dp[n][m]);
    return 0;
}
  • 40
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木又可可

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值