象棋中的马跳步问题

news2024/11/15 10:04:32

象棋中的马跳步问题

作者:Grey

原文地址:

博客园:象棋中的马跳步问题

CSDN:象棋中的马跳步问题

题目描述

中国象棋中,整个棋盘就是横坐标上 9 条线、纵坐标上 10 条线的一个区域,给你三个 参数 x,y,k;返回『马』从 (0,0) 位置出发,必须走 k 步;

最后落在 (x,y) 上的方法数有多少种?

题目链接见:牛客-象棋中马的跳法

暴力解法

定义递归函数

int ways(int i, int j, int a, int b, int step)

递归含义表示:从 (i,j) 出发,到 (a,b) 且必须要走 step 步的情况下,有多少种走法。

接下来是 base case,首先 (i,j) 坐标如果已经越界,说明不可能有有效走法,直接返回 -1。

(i, j) 越界的条件是

(i >= 10 || j >= 9 || i < 0 || j < 0)

如果 step == 0,说明没有可走的步数了,此时,除非 (i == a && j == b) ,可以有一种走法(在原地不动),其他情况,都无路可走,返回 -1。

base case 代码如下

        // 象棋区域 int[][] area = new int[10][9]
        if (i >= 10 || j >= 9 || i < 0 || j < 0) {
            // 越界
            return -1;
        }
        if (step == 0) {
            if (i == a && j == b) {
                return 1;
            }
            return -1;
        }

接下来就是普遍情况,『马』可以四面八方尝试

    // 四面八方尝试
        int p1 = ways(i - 2, j + 1, a, b, step - 1);
        int p2 = ways(i - 1, j + 2, a, b, step - 1);
        int p3 = ways(i - 1, j - 2, a, b, step - 1);
        int p4 = ways(i - 2, j - 1, a, b, step - 1);
        int p5 = ways(i + 2, j + 1, a, b, step - 1);
        int p6 = ways(i + 1, j + 2, a, b, step - 1);
        int p7 = ways(i + 1, j - 2, a, b, step - 1);
        int p8 = ways(i + 2, j - 1, a, b, step - 1);

返回这些情况的合计即可。

暴力解法完整代码如下

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x = in.nextInt();
        int y = in.nextInt();
        int k = in.nextInt();
        System.out.println(ways(0,0,x, y, k));
        in.close();
    }

    // 递归含义:还剩下step步,从(i,j)到达(a,b)可以选择的方法数是多少
    public static int ways(int i, int j, int a, int b, int step) {
        // 象棋区域 int[][] area = new int[10][9]
        if (i >= 10 || j >= 9 || i < 0 || j < 0) {
            // 越界
            return -1;
        }
        if (step == 0) {
            if (i == a && j == b) {
                return 1;
            }
            return -1;
        }
        // 四面八方尝试
        int p1 = ways(i - 2, j + 1, a, b, step - 1);
        int p2 = ways(i - 1, j + 2, a, b, step - 1);
        int p3 = ways(i - 1, j - 2, a, b, step - 1);
        int p4 = ways(i - 2, j - 1, a, b, step - 1);
        int p5 = ways(i + 2, j + 1, a, b, step - 1);
        int p6 = ways(i + 1, j + 2, a, b, step - 1);
        int p7 = ways(i + 1, j - 2, a, b, step - 1);
        int p8 = ways(i + 2, j - 1, a, b, step - 1);
        return ((p1 == -1) ? 0 : p1) + ((p2 == -1) ? 0 : p2) + ((p3 == -1) ? 0 : p3) + ((p4 == -1) ? 0 : p4) + ((p5 == -1) ? 0 : p5) + ((p6 == -1) ? 0 : p6) + ((p7 == -1) ? 0 : p7) + ((p8 == -1) ? 0 : p8);
    }
}

运行超时

image

动态规划解(可 AC)

根据上述暴力递归过程可知,递归函数有三个可变参数,分别是 a,b,step,每个参数都有一定的范围,所以可以利用一个三维数组 dp 来囊括所有的递归过程的中间结果。

        // 象棋区域 int[][] area = new int[10][9]
        int[][][] dp = new int[10][9][step + 1];

其中dp[x][y][k]就表示递归函数ways(0,0,x,y,k)的结果。

基于暴力递归的 base case 可知

dp[a][b][0] = 1;

针对普遍情况,暴力递归过程的伪代码如下

    public static int ways(int i, int j, int a, int b, int step) {
        ……
        // 四面八方尝试
        int p1 = ways(i - 2, j + 1, a, b, step - 1);
        int p2 = ways(i - 1, j + 2, a, b, step - 1);
        int p3 = ways(i - 1, j - 2, a, b, step - 1);
        int p4 = ways(i - 2, j - 1, a, b, step - 1);
        int p5 = ways(i + 2, j + 1, a, b, step - 1);
        int p6 = ways(i + 1, j + 2, a, b, step - 1);
        int p7 = ways(i + 1, j - 2, a, b, step - 1);
        int p8 = ways(i + 2, j - 1, a, b, step - 1);
        ……
    }

dp[i][j][step] 依赖 dp[i-2][j+1][step-1]dp[i-1][j+2][step-1]dp[i-1][j-2][step-1]dp[i-2][j-1][step-1]dp[i+2][j+1][step-1]dp[i+1][j+2][step-1]dp[i+1][j-2][step-1]dp[i+2][j-1][step-1] ,示例图如下

如下图,其中(i,j,step)坐标上的点只依赖 step - 1 层上对应的八个点,而不依赖本层任意一点。

image

已知第 0 层已经填好了(上面已经提到 dp[a][b][0] = 1 ),所以,可以从 1 层开始,依次填好每一层。

动态规划解完整代码如下


import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x = in.nextInt();
        int y = in.nextInt();
        int k = in.nextInt();
        System.out.println(ways(x, y, k));
        in.close();
    }

    // 根据暴力递归改动态规划
    public static int ways(int a, int b, int step) {
        // 象棋区域 int[][] area = new int[10][9]
        int[][][] dp = new int[10][9][step + 1];
        dp[a][b][0] = 1;
        for (int k = 0; k < step + 1; k++) {
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 9; j++) {
                    if (k == 0) {
                        if (i == a && j == b) {
                            dp[i][j][k] = 1;
                        } else {
                            dp[i][j][k] = -1;
                        }
                    } else {
                        int p1 = (i - 2 >= 0 && j + 1 < 9) ? dp[i - 2][j + 1][k - 1] : -1;
                        int p2 = (i - 1 >= 0 && j + 2 < 9) ? dp[i - 1][j + 2][k - 1] : -1;
                        int p3 = (i - 1 >= 0 && j - 2 >= 0) ? dp[i - 1][j - 2][k - 1] : -1;
                        int p4 = (i - 2 >= 0 && j - 1 >= 0) ? dp[i - 2][j - 1][k - 1] : -1;
                        int p5 = (i + 2 < 10 && j + 1 < 9) ? dp[i + 2][j + 1][k - 1] : -1;
                        int p6 = (i + 1 < 10 && j + 2 < 9) ? dp[i + 1][j + 2][k - 1] : -1;
                        int p7 = (i + 1 < 10 && j - 2 >= 0) ? dp[i + 1][j - 2][k - 1] : -1;
                        int p8 = (i + 2 < 10 && j - 1 >= 0) ? dp[i + 2][j - 1][k - 1] : -1;
                        dp[i][j][k] = (p1 == -1 ? 0 : p1) + (p2 == -1 ? 0 : p2) + (p3 == -1 ? 0 : p3) + (p4 == -1 ? 0 : p4) + (p5 == -1 ? 0 : p5) + (p6 == -1 ? 0 : p6) + (p7 == -1 ? 0 : p7) + (p8 == -1 ? 0 : p8);
                    }
                }
            }
        }
        return dp[0][0][step];
    }

}

更多

算法和数据结构笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计springboot+vue基本微信小程序的汽车租赁公司小程序

项目介绍 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时…

【Vue】环境搭建

Vue 简介&#xff1a; 一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方面&#xff0c;当与现代化的工具链以…

Jenkins+Docker 一键自动化部署 SpringBoot 项目

本文章实现最简单全面的 Jenkins Docker Spring Boot 一键自动部署项目。步骤齐全&#xff0c;少走坑路。 环境&#xff1a;CentOS7 Git (Gitee) 实现步骤&#xff1a;在 Docker 安装 Jenkins&#xff0c;配置 Jenkins 基本信息&#xff0c;利用 Dockerfile 和 Shell 脚本实…

CSS 的布局 盒子

目录 块级元素和行内元素 块级元素 特点&#xff1a; 行内元素 特点&#xff1a; 行内元素 和 块级元素 的区别 改变显示模式&#xff08;把行内元素 变成 块级元素) 盒模型 盒模型图 边框基础属性 内边框、外边框 基础写法&#xff1a; 复合写法 块级元素水平居中 前提&#…

C++知识点大全(第二版)

目录 1 C简介 1.1 起源 1.2 应用范围 1.3 C和C 2开发工具 3 基本语法 3.1 注释 3.2关键字 3.3标识符 4 数据类型 4.1基本数据类型 4.2 数据类型在不同系统中所占空间大小 4.3 typedef声明 4.4 枚举类型 5 变量 5.1 变量的声明和定义 5.2 变量的作用域 6 运算…

如何当好硬软件助理工程师——实习周报(一)

如何当好硬软件助理工程师——实习周报 如何当好硬软件助理工程师——实习周报&#xff08;一&#xff09; 文章目录如何当好硬软件助理工程师——实习周报前言一、问题积累1.git 指令2.Coding的使用3.代码中的知识点&#xff08;HAL库、Flash读写&#xff09;a.gpio配置b.fla…

程序员必备的画图工具汇总

文章目录1、简介2、draw.io3、Excalidraw4、Xmind5、语雀6、Database6.1 dbdiagram.io6.2 sqldbm6.3 QuickDBD7、UML7.1 plantuml7.2 Graphviz结语1、简介 优秀的作图工具有许多&#xff0c;例如文本绘图工具 PlantUML&#xff0c;流程图设计工具 Draw.io&#xff0c;还有专业…

ORA-01861: literal does not match format string

系统&#xff1a;AnolisOS7.9 数据库&#xff1a;Oracle11.2.0.4 问题描述&#xff1a;执行TSPITR时&#xff0c;报错ORA-01861&#xff0c;如下所示&#xff1a; [oracleliujun~]$ export NLS_DATE_FORMATyyyy-mm-dd hh24:mi:ss [oracleliujun~]$ export NLS_LANGAMERICAN…

手写Mybatis源码(原来真的很简单!!!)

目录一、JDBC操作数据库_问题分析二、自定义持久层框架_思路分析三、自定义框架_编码1、加载配置文件2、创建两个配置类对象3、解析配置文件&#xff0c;填充配置类对象4、创建SqlSessionFactory工厂接口及DefaultSqlSessionFactory实现类5、创建SqlSession会话接口及DefaultSq…

R语言和医学统计学(10):正态性和方差齐性检验

本文首发于公众号&#xff1a;医学和生信笔记&#xff0c;完美观看体验请至公众号查看本文。 医学和生信笔记&#xff0c;专注R语言在临床医学中的使用&#xff0c;R语言数据分析和可视化。 文章目录前言正态性检验shapiro wilk检验kolmogorov smimov检验方差齐性检验两样本方差…

SSH框架重构需求,起码读懂代码和前端的原生JS

文章目录SSH(Spring Struts Hibernate)商城老项目JSPHtmlJsJquery 老项目部署Tomcat上面进行部署可以一次性部署多个项目1.Spring2.Struts3.Hibernate(持久层一个重量级框架)4.里面的请求路径解析SSH(Spring Struts Hibernate)商城老项目JSPHtmlJsJquery 老项目部署 Tomca…

【JavaSE之JDK8新特性】三万字详文带你了解JDK8新特性

JDK8新特性一、Lambda1.1需求分析2.Lambda表达式的初级体验3.Lambda表达式的语法规则3.1.Lambda练习13.2.Lambda表达式练习24.FunctionalInterfa注解说明5.Lambda表达式的原理6.Lambda表达式的省略写法7.lambda表达式的使用前提8.lambda和匿名内部类的对比二、接口中新增的方法…

#define宏的妙用!实现你以为的函数offsetof等

目录 一.#define 1.1#define的使用 1.2在define定义标识符的时候&#xff0c;要不要在最后加上 ;? 二.#define定义宏 2.1好代码的写法: 2.2#define 替换规则 2.3#和## 三.带有副作用的宏参数 四.宏和函数的对比 五.实现offetof 思路解析过程&#xff1a; 代码实现&am…

破解系统密码

一、利用5次shift漏洞破解win7密码 1.1 漏洞 1. 在未登录时&#xff0c;连续按5次shift键&#xff0c;弹出程序C:\Windows\System32\sethc.exe 2. 部分win7及win10系统在未进入系统时&#xff0c;可以通过系统修复漏洞篡改系统文件名&#xff01; 注意&#xff1a;如win7或win…

SpringBoot/Spring AOP默认动态代理方式

Spring 5.x中AOP默认依旧使用JDK动态代理SpringBoot 2.x开始&#xff0c;AOP为了解决使用JDK动态代理可能导致的类型转换异常&#xff0c;而使用CGLIB。在SpringBoot 2.x中&#xff0c;AOP如果需要替换使用JDK动态代理可以通过配置项spring.aop.proxy-target-classfalse来进行修…

【Linux】7.0 信号

文章目录信号的基本概念kill -l 查看信号列表信号的处理方式signal( ) 自定义处理信号信号的产生方式键盘产生进程异常&#xff08;core dump&#xff09;系统调用软件条件信号的发送&#xff08;OS&#xff09;信号常见相关名词解释进程接收处理信号原理信号集函数的使用打印p…

【Redis】3.详解分布式锁

文章目录1. 什么是分布式锁2. 分布式锁的特点3. 常见的分布式锁4. 实现分布式锁5.解决分布式锁中的原子性问题5.1 Lua脚本5.2 使用Java代码调用Lua脚本实现原子性1. 什么是分布式锁 分布式锁是指分布式系统或者不同系统之间共同访问共享资源的一种锁实现&#xff0c;其是互斥的…

【Django框架】——20 Django视图 02 路由命名和反向解析

文章目录一、 路由命名二、reverse反向解析三、通过URL模板页面进行传参四、namespace1.reverse反向解析2.url模板标签在 Django 项⽬中&#xff0c;⼀个常⻅需求是获取最终形式的 URL&#xff0c;⽐如⽤于嵌⼊⽣成的内容中&#xff08;视图和资源⽹址&#xff0c;给⽤户展示⽹…

《网络安全笔记》第七章:注册表基础

一、注册表基础 1、概述 注册表是windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心“数据库”&#xff0c;也可以说是一个非常巨大的树桩分层结构的数据库系统注册表记录了用户安装在计算机上的软件和每个程序的相互关联信息&#xff0c;它包括了计算…

【UDS】ISO14229之0x2F服务

文章目录前言一、理论描述二、使用步骤1.请求2.响应总结->返回总目录<- 前言 简称&#xff1a; “InputOutputControlByIdentifier”&#xff0c;根据标识符控制输入输出 功能&#xff1a; 根据标识符控制输入输出服务用于替换输入信号的值、电控单元内部参数或控制电子…