题目
过河卒
动态规划
思路
我们知道,小卒只能朝右或者下走,先假设没有对方的马。那么对于起点所在的行上的所有位置就只有一种不同路径,因为那些位置只能从左边到达(转换思维),同样那么对于起点所在的列上的所有位置也只有一种不同路径,因为那些位置只能从上边到达。所以,我们可以得到以下思路:
建立一个 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[i−1][j]+dp[i][j−1]
上面的思路针对没有对方的马的情况,但是这道题存在对方的一匹马,那又该怎么办呢?
我们知道,小卒不能通过对方马的控制点,也就是说,如果从左上角到右下角只要遇到控制点,那么控制点之右和控制点之下的位置就都不能到达了。而控制点本身对不同路径数目的贡献为 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;// 初始时全部置为-1for (i = 0; i <= n; i++) {for (j = 0; j <= m; j++) {dp[i][j] = -1;}}// 然后将马能到达的点全部置为0dp[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;}}}// 同样,对于第一列(除起点外)也采用同样的方式处理为 1for (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;
}