P1002 过河卒 详细解析

P1002 [NOIP2002 普及组] 过河卒

题目描述

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

棋盘用坐标表示, A A A ( 0 , 0 ) (0, 0) (0,0) B B B ( n , m ) (n, m) (n,m),同样马的位置坐标是需要给出的。

现在要求你计算出卒从 A A A 点能够到达 B B B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 B B B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

样例 #1

样例输入 #1

6 6 3 3

样例输出 #1

6

提示

对于 100 % 100 \% 100% 的数据, 1 ≤ n , m ≤ 20 1 \le n, m \le 20 1n,m20 0 ≤ 0 \le 0 马的坐标 ≤ 20 \le 20 20

思路

根据题意,卒只能向右或者向下走,题目中所要求的是到达B点的可行路径条数,我们知道,到达B点的可行路径条数等于紧挨着B点的左边的点对应的路径条数加上走到B点上面的点的可行路径条数,于是我们可以找到一个递推公式

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

既然得到了递推公式,我们首先考虑的方法便是动态规划,我们创造一个数组dp
dp[i][j]代表走到(i,j)这个点的可行路径条数,
需要注意的是,题目中的马及马可以一步走到的地方是不可以让卒通过的,其对应的点的可行路径条数是0,为什么呢?我们可以考虑这个不可走的点的下一个点(遍历的下一个点),对于下一个点来说,卒没办法从这个不可走的点过来,所以对应的条数为0。
设马的坐标为(k.p),那么就可以得到

    dp[k][p] = 0;
    dp[k+1][p+2] = 0;
    dp[k+2][p+1] = 0;
    dp[k+2][p-1] = 0;
    dp[k+1][p-2] = 0;
    dp[k-1][p+2] = 0;
    dp[k-2][p-1] = 0;
    dp[k-2][p+1] = 0;
    dp[k-1][p-2] = 0;

接下来初始化dp数组。先考虑边界的情况,x和y轴上的点对应的可行路径只有一条,因为x,y轴只对应着一条路径。所以对于x轴上的点可以得到

for(int i = 0; i <= n; i++){   
    dp[i][0] = 1;
}

同理对于y轴上的点有

for(int j = 0; j <= m; j++){
    dp[0][j] = 1;
}

递推部分的代码也很容易得到,如果这个点可以走,那么进行递推,如果走不了,就直接跳过这个点

for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(dp[i][j] != 0  ){
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
                
            }
            else{
            	continue; 	
	            } 
        }
    }

由于我们对不可走的点赋值为0,所以初始化整个棋盘的时候不能全部初始化为0,我们可以初始化为-1

for(int i = 0; i < 50; i++){
    	for(int j = 0; j < 50; j++){
    		dp[i][j] = -1;
		}
	}

但问题来了,按照上述代码构造出的题解存在问题!
我们可以设想这种情况,马一步走到的位置如果在边界上,那么这个点的可行路径条数一定是0,但是这个点后面的点仍然被赋值为1
如果一个不可行点在边界上,那么其后面的点一定不可以被走到(因为卒只能向左或者向下走)所以我们就需要对边界赋值的代码进行修改

for(int i = 0; i <= n; i++){
    	if(dp[i][0] == 0){
    		for(int c = i; c <= n; c++){
    			dp[c][0] = 0;
			}
		}
        else{
        	dp[i][0] = 1;
		}
    }

y轴同理

完整代码如下

#include<iostream>
using namespace std;

int main(){
    int m,n,k,p;
    cin>>n>>m>>k>>p;
    for(int i = 0; i < 50; i++){
    	for(int j = 0; j < 50; j++){
    		dp[i][j] = -1;
		}
	}
	dp[k][p] = 0;
    dp[k+1][p+2] = 0;
    dp[k+2][p+1] = 0;
    dp[k+2][p-1] = 0;
    dp[k+1][p-2] = 0;
    dp[k-1][p+2] = 0;
    dp[k-2][p-1] = 0;
    dp[k-2][p+1] = 0;
    dp[k-1][p-2] = 0;
    for(int i = 0; i <= n; i++){
    	if(dp[i][0] == 0){
    		for(int c = i; c <= n; c++){
    			dp[c][0] = 0;
			}
		}
        else{
        	dp[i][0] = 1;
		}
    }
    for(int j = 0; j <= m; j++){
    	if(dp[0][j] == 0){
    		for(int k = j; k <= m; k++){
    			dp[0][k] = 0;
			}
		}
        else{
        	dp[0][j] = 1;
		}
    }
    
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(dp[i][j] != 0  ){
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
                
            }
            else{
            	continue; 	
			} 
        }
    }
    cout << dp[n][m];
    return 0;
}

有错误请指出~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是C语言洛谷p1002过河卒的代码实现和注释解析: ```c #include <stdio.h> #define MAX 21 // 定义最大值 int main() { int n, m, xh, yh, i, j, k; long long map[MAX][MAX] = {0}; // 定义地图,初始化为0 scanf("%d %d %d %d", &n, &m, &xh, &yh); // 输入信息 map[xh][yh] = -1; // 标记障碍物 for (k = 0; k < 3; k++) { // 遍历3个方向 switch (k) { case 0: i = 2; j = 1; break; // 方向1 case 1: i = 1; j = 2; break; // 方向2 case 2: i = 1; j = 1; break; // 方向3 } if (xh - i >= 0 && yh - j >= 0) { // 判断是否越界 map[xh - i][yh - j] = -1; // 标记障碍物 } if (xh + i <= n && yh + j <= m) { // 判断是否越界 map[xh + i][yh + j] = -1; // 标记障碍物 } } map[0][0] = 1; // 起点为1 for (i = 0; i <= n; i++) { // 遍历行 for (j = 0; j <= m; j++) { // 遍历列 if (map[i][j] != -1) { // 判断是否为障碍物 if (map[i - 1][j] != -1) { // 上可通过 map[i][j] += map[i - 1][j]; // 累加上方格子的值 } if (map[i][j - 1] != -1) { // 左可通过 map[i][j] += map[i][j - 1]; // 累加左方格子的值 } } } } printf("%lld\n", map[n][m]); // 输出结果 return 0; } ``` 注释解析: 1. 定义了一个二维数组`map`,用于存储地图信息,初始化为0。 2. 输入了4个整数,分别为地图的行数`n`、列数`m`、卒的初始位置`xh`、`yh`。 3. 标记了障碍物,即卒不能到达的位置。 4. 遍历了3个方向,分别为向上走2步、向左走1步;向上走1步、向左走2步;向上走1步、向左走1步。如果卒可以到达该位置,则标记为障碍物。 5. 起点的值为1,表示只有一种走法。 6. 遍历整个地图,如果该位置不是障碍物,则累加上方格子和左方格子的值。 7. 输出结果,即终点的值。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值