【递归与递推】洛谷[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

目录
相关文章
|
SQL 数据库
SQL INSERT INTO SELECT 语句
SQL INSERT INTO SELECT 语句
1608 8
|
编译器 C++
【Qt】信号槽的三种连接形式
三种形式的信号槽连接,connect 语法...
338 0
|
Serverless
函数计算FC怎么计费?
函数计算FC怎么计费?
621 1
单字节,双字节,四字节能够表示的数值大小范围分别是多少
单字节,双字节,四字节能够表示的数值大小范围分别是多少
|
开发框架 安全 数据库
Flask vs. Django
【5月更文挑战第9天】对比了 Flask 和 Django 两个流行 Web 框架。Flask 轻量级,适用于小型到中型应用,强调简单和灵活性;Django 全栈,适合大型应用,内置功能丰富。Flask 在性能上通常更快,适合高并发场景,而 Django 在处理复杂数据模型时效率更高。两者生态系统活跃,Flask 部署简单,Django 部署复杂但扩展性强。Django 安全性出色,Flask 需额外扩展增强安全。在数据库支持上,Django 内置 ORM,支持多种数据库。选择框架需综合考虑多方面因素。
|
C语言
C语言取整方法详解
C语言取整方法详解
2370 0
|
存储 前端开发 JavaScript
最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)
最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)
194 2
|
弹性计算 关系型数据库 数据库
在阿里云上搭建高效Web服务的完整指南
构建高效、稳定的Web服务是每个开发者的必修课。本文将详细介绍如何基于阿里云的相关产品,搭建一个具有高可用性和强大性能的Web服务。我们将使用Elastic Compute Service(ECS)、Server Load Balancer(SLB)、Relational Database Service(RDS)、域名服务等阿里云产品,通过图文并茂的方式为你展示整个流程。
459 0
|
弹性计算 Linux 数据中心
阿里云香港云服务器优惠价格_香港服务器_新世界云服务器_香港vps
阿里云香港云服务器优惠价格_香港服务器_新世界云服务器_香港vps,阿里云香港服务器2核1G、30M带宽、40GB ESSD系统盘优惠价格24元/月,288元一年,每月流量1024GB
|
消息中间件 关系型数据库 MySQL
使用Flume实现MySQL与Kafka实时同步
使用Flume实现MySQL与Kafka实时同步