【递归与递推】洛谷[NOIP2002 普及组] 过河卒

简介: 前言 本题来自洛谷P1002. 题目链接:[NOIP2002 普及组] 过河卒 - 洛谷

题目描述

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

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

截屏2023-04-23 20.49.30.png

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

输入格式

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

输出格式

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

输入输出样例

输入

6 6 3 3

输出

6

说明/提示

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

【题目来源】

NOIP 2002 普及组第四题

 

解题思路

      乍看一眼这道题好像没有什么思路,那我们就试着找一找前面的方法数是多少,看看有没有什么规律,或者是否能够写出一个递推式,我在下图中把一些距离A点较近的方法数标了出来,看看有什么规律?

截屏2023-04-23 20.56.10.png

递推式的求取:我首先把A点所在行的前四列和A点所在列的前四行的方法数标记了出来,这个很好理解吧?因为过河卒只能向下或向右走所以一直向下或者一直向右的方法数只能有一种。那么接下来我们再看看内部的点的方法数。

截屏2023-04-23 20.56.56.png

什么?你对这些方法数的算法有点懵?没事,且听我慢慢道来。我先说结论吧,其实要算到达某一点的方法数,其实就是组合问题,大家应该都学过排列组合吧?注意这里是组合而不是排列。因为这个组合数是对方向的组合,就是说我在我现在的位置可以怎么走呢?是不是只有向右或向下两种方法数,而对于某个任意点,这里为了方便表述,就拿当前图中马的所在点C举例吧(但马的位置不一定在这里哦,这个是需要用户输入的)。要到达C点无论走多少步,是不是至少得有两个向下走的和四个向右走的?过河卒肯定不会多走,或者说过河卒肯定是走到达某一点的最短路径的。所以说我们可以从这些信息中得知,要到达C点,总步数为6步,而其中有2步必须向下,剩下的4步必须向右,相信,这一定难不倒大家了吧?就是在6步之中选择两步,这两步都是向下所以没有差异,是组合问题,所以可以知道从A点到达C点的方法数为截屏2023-04-23 20.59.08.png

也就是15种,其他点的方法数可以像C点这样求出来,但是要是都这样算是不是太麻烦了?而且我们还没有加入马的控制点,能不能从已有的这些数据中找出一些规律呢?细心的朋友可能已经发现了就是对于内部的点到达任意一个点的方法数等于到达它上面点的方法数再加上到达它左面点方法数(而对于边界上的点(比如A点所在行上的点的方法数只取决于它左边点的方法数,而对于A点所在列上的点的方法数只取决于它上面点的方法数,这个我们编码的时候特殊处理一下就好)),很容易,我们能写出递推式来:f[i][j]=f[i-1][j]+f[i][j-1].


好,到此我们的问题已经解决了一半了,剩下的就是要来处理马的控制点。


马的控制点的处理:如果过河卒走到了马的控制点以后,他将不能再继续往下走,无论上下左右,所以我们必须要避开马的控制点。怎么避开呢?就是要找出马的控制点然后标记出来,将走到它的方法数置为0,说明我们走不到这点。


具体标记方法:对于所有马的控制点最多就是八个(再加马的位置),如图中所示,但是我们对于不同的马的位置要看看马的控制点有几个在我们的范围内,对于马的控制点我们先按照最多的来找,然后找出来再来判断它是否在范围内,找法:我们可以定义一个方向数组,因为对于给定的马的位置,其控制点的位置是确定的,而这个方向数组就是存放马的位置的偏移量,只有马的位置给出,再加上它的偏移量就可以得到对应的控制点,最后来判断控制点是否在范围内,如果在范围内就标记出来,否则无需进行其他操作。


来看看具体代码


#include <iostream>

using namespace std;

long long f[21][21];//方法数数组,防止超出数据范围定义为长整型

bool is[21][21];//判断是否为马的控制点的数组,若为控制点更新为true

//定义方向数组分别代表P1~P8

int dx[]={2,1,-1,-2,-2,-1,1,2};//横坐标的偏移量

int dy[]={1,2,2,1,-1,-2,-2,-1};//纵坐标的偏移量

int main()

{  int n,m,mx,my;

  cin>>n>>m>>mx>>my;

  is[mx][my]=true;//注意不要漏掉马的位置,马所在的位置也算马的控制点

  //标记马的控制点

  for(int i=0;i<8;i++){

   //判断马的控制点是否在范围内,若在范围内,标记

   if(mx+dx[i]>=0&&mx+dx[i]<=n&&my+dy[i]>=0&&my+dy[i]<=m){

    is[mx+dx[i]][my+dy[i]]=true;

   }    

  }

  f[0][0]=1;//A点到达A点的方法数为1,就是“不走”

  //对于第一行和第一列,我们要将其设为边界,方便下面的递推

  for(int j=1;j<=m;j++){

      f[0][j]=f[0][j-1];

//判断是否为马的控制点,若是,将方法数置0

if(is[0][j]){

 f[0][j]=0;

}

  }

  //第一列同上面一样的做法

  for(int i=1;i<=n;i++){

     f[i][0]=f[i-1][0];

   if(is[i][0]){

 f[i][0]=0;

}  

  }

  //对于内部的点利用递推式 f[i][j]=f[i-1][j]+f[i][j-1]

  for(int i=1;i<=n;i++){

   for(int j=1;j<=m;j++){

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

    if(is[i][j]){

     f[i][j]=0;

    }

   }

  }

  cout<<f[n][m];

  return 0;

}

截屏2023-04-23 21.01.11.png

目录
相关文章
|
9月前
[NOIP2002]过河卒 标准递归
[NOIP2002]过河卒 标准递归
54 6
|
9月前
|
C++
【洛谷 P1044】[NOIP2003 普及组] 栈 题解(递归+记忆化搜索)
**NOIP2003普及组栈问题**:给定操作数序列1到n,仅允许push(进栈)和pop(出栈)操作。目标是计算所有可能的输出序列总数。输入包含一个整数n(1≤n≤18)。示例输入3,输出5。当队列空时返回1,栈空则只能入栈,栈非空时可入栈或出栈。AC C++代码利用记忆化搜索求解。
106 1
|
9月前
【洛谷 P1002】[NOIP2002 普及组] 过河卒 题解(递归+记忆化搜索)
`NOIP2002`普及组的过河卒问题是一个棋盘路径计数挑战。卒从$(0,0)$出发到$(n,m)$,只能向下或向右移动,马在$(c1,c2)$固定,控制某些点。任务是计算不受马阻挡的路径数。输入是目标和马的位置,输出是路径总数。使用动态规划和记忆化搜索避免重复计算,样例输入$(6,6,3,3)$输出$6$。代码中定义了$f(x,y)$计算$(x,y)$处的路径数,利用边界条件和递推关系计算。
107 0
|
9月前
|
C++
【洛谷 P1047】[NOIP2005 普及组] 校门外的树 题解(位集合)
**NOIP2005普及组问题:**给定长度为$l$的马路,上面等距种植着树,需移除位于建造地铁区域的树。输入包含马路长度和区域数,以及各区域起止点,输出移树后剩余树的数量。样例输入:$l=500$, $m=3$,输出:$298$。$20\%$数据无区域重合,$1 \leq l \leq 10^4$,$1 \leq m \leq 100$。解决方案利用位集合(bitset)表示树的状态,遍历区域将树设为0,最后统计1的数量。AC代码使用C++实现。
56 0
|
9月前
【洛谷 P1093】[NOIP2007 普及组] 奖学金 题解(结构体排序)
**NOIP2007普及组奖学金问题**:根据学生语文、数学、英语三科成绩计算总分并排序。若总分相同,按语文成绩高者优先,再相同则学号小者靠前。程序需输出前5名学生的学号和总分。输入包括学生人数`n`和每人的三科成绩,输出为5行结果。示例输入和输出已给出,代码通过定义结构体和自定义比较器实现排序。
68 0
|
9月前
【洛谷 P2669】[NOIP2015 普及组] 金币 题解(循环)
`NOIP2015`普及组题目,骑士按周期领金币:第一天1枚,随后$n$天每天$n$枚,然后$n+1$天每天$n+1$枚。给定天数$k$,求总金币数。输入$k$,输出金币总数。样例输入6,输出14;输入1000,输出29820。代码使用循环和变量控制周期,累计金币数。
200 0
|
9月前
|
C++
【洛谷 P1075】[NOIP2012 普及组] 质因数分解 题解(判断质数)
NOIP2012普及组题目,给定合数$n$由两个不同质数乘积组成,求较大质数。输入一个正整数$n$,输出较大质因子。例如输入21,输出7。代码使用C++,通过循环和质数判断找到能整除$n$的质数,再求另一个质数,并输出较大者。
106 0
|
9月前
|
算法
【洛谷 P1003】[NOIP2011 提高组] 铺地毯 题解(数组+贪心算法)
**NOIP2011 提高组问题:铺地毯** 在第一象限的颁奖典礼场地,有$n$张地毯按编号顺序铺设。需找出覆盖特定点$(x, y)$的最上方地毯编号。输入包括地毯坐标和点坐标,输出地毯编号或-1表示未覆盖。 样例:给定3张地毯,点$(2,2)$被第3张地毯覆盖,输出3;另一样例点$(4,5)$未被覆盖,输出-1。 $30\%$数据$n\leq2$,$50\%$数据坐标$\leq100$,$100\%$数据$n\leq10^4$,坐标$\leq10^5$。 解决方法:从下到上遍历地毯,更新覆盖点的地毯编号。 AC代码略。
91 0
|
9月前
【洛谷 P1909】[NOIP2016 普及组] 买铅笔 题解(打擂台法)
**摘要:** P老师需买$n$支铅笔作礼物,商店有3种包装(数量、价格不等),不能拆包。目标是最少花费。输入包括$n$和每种包装的详情,输出最小花费。样例展示最优选择过程。代码使用打擂台法求解,读入$n$和包装信息,计算每种包装的最小花费,取最小值输出。
100 0
|
存储 人工智能 测试技术
第十四届蓝桥杯省赛JavaB组试题E【蜗牛】Dijkstra堆优化 or 线性DP?
第十四届蓝桥杯省赛JavaB组试题E【蜗牛】Dijkstra堆优化 or 线性DP?
172 0
第十四届蓝桥杯省赛JavaB组试题E【蜗牛】Dijkstra堆优化 or 线性DP?