算法27:从暴力递归到动态规划(2)

news2025/2/24 10:25:00

上一题比较简单,下面来一道比较难的题目。


假设有排成一行的N个位置,记为1~NN 一定大于或等于 2

开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)

如果机器人来到1位置,那么下一步只能往右来到2位置;

如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;

如果机器人来到中间位置,那么下一步可以往左走或者往右走;

规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种

给定四个参数 NMKP返回方法数。

解题思路:

1. 如果机器人在1的位置,只能从左到右

2.如果机器人在N的位置,只能从右到左

3.如果机器人在中间,既可以往右、也可以往左

现在的题目是要求返回多少种走法,也就是只要能到达目的地,并且步数足够,我们随意走都可以。

遇到这种题目,绘图是最好的方式:

假设一共有6个位置,从2位置出发,走5步,到达位置5,一共有多少种走法

 

递归方式 

package code03.动态规划_07;

/**
 * 假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2
 * 开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)
 * 如果机器人来到1位置,那么下一步只能往右来到2位置;
 * 如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;
 * 如果机器人来到中间位置,那么下一步可以往左走或者往右走;
 * 规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
 * 给定四个参数 N、M、K、P,返回方法数。
 */
public class Robot_02 {
    /**
     *
     * @param mStart   M 开始位置
     * @param kSteps   K 步数
     * @param nAll     N 长度
     * @param pAim     P 目的地位置
     * @return
     */
    public static int way1(int mStart, int kSteps, int nAll, int pAim)
    {
        return process(mStart, kSteps, nAll, pAim);
    }

    public static int process ( int mStart, int kSteps, int nAll, int pAim)
    {
        //步数为0,没法走
        if (kSteps == 0) {
           return mStart == pAim ? 1 : 0;
        }
        //只能往右走
        if (mStart == 1) {
            return process(mStart + 1, kSteps-1, nAll, pAim);
        }
        //只能往左走
        if (mStart == nAll) {
            return process(nAll-1, kSteps-1, nAll, pAim);
        }
        //中间位置,即可往左,也可往右,需要统计出往左往右的和
        return process(mStart +1, kSteps-1, nAll, pAim)
                +  process( mStart -1, kSteps-1, nAll, pAim);
    }

    public static void main(String[] args) {
        System.out.println(way1(2,5,6,5));
    }
}

同样的问题,我们分析可以得到:

在2位置,剩余5步,有5中走法

在3位置,剩余4步,有4种走法

在4位置,剩余3步,有3种走法

在5位置,剩余2步,有2种走法;

在6位置,剩余1步,有1种走法

中间存在来来回回重复的走法,这说明我们存在重复的推导结果,自然就过渡到递归+动态规划的方式进行优化了。

递归 + 动态规划

package code03.动态规划_07;

/**
 * 假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2
 * 开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)
 * 如果机器人来到1位置,那么下一步只能往右来到2位置;
 * 如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;
 * 如果机器人来到中间位置,那么下一步可以往左走或者往右走;
 * 规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
 * 给定四个参数 N、M、K、P,返回方法数。
 */
public class Robot_02_opt1 {
    /**
     * @param mStart   M 开始位置  [0-nAll]
     * @param kSteps   K 步数
     * @param nAll     N 长度
     * @param pAim     P 目的地位置
     * @return
     */
    public static int way1(int mStart, int kSteps, int nAll, int pAim)
    {
        /**
         * 位置是1-N, 每一个位置都存在不同的走法,以每个位置为行,那么行的范围就是1-N
         * 由于java数组下标从0开始,想要获取1-N范围,长度需要加1
         *
         * 我们将剩余步数为列,这样每个位置剩余步数就可以很直观的观察到
         */
        int[][] dp = new int[nAll + 1][kSteps+1]; //剩余步数为横坐标,当前位置为纵坐标
        for (int i = 0; i < dp.length; i++) {
            for(int j = 0; j <dp[i].length; j++) {
                dp[i][j] = -1;
            }
        }
        int result = process(mStart, kSteps, nAll, pAim, dp);
        return process(mStart, kSteps, nAll, pAim, dp);
    }

    public static int process ( int mStart, int kSteps, int nAll, int pAim, int[][] dp)
    {
        if (dp[mStart][kSteps] != -1) {
            return dp[mStart][kSteps];
        }

        int result;
        if (kSteps == 0) {
            result = mStart == pAim ? 1 : 0;
        }
        //只能往右走
        else if (mStart == 1) {
            result =  process(mStart + 1, kSteps-1, nAll, pAim, dp);
        }
        //只能往左走
        else if (mStart == nAll) {
            result = process(nAll-1, kSteps-1, nAll, pAim, dp);
        }
        //中间位置,即可往左,也可往右,需要统计出往左往右的和
        else{
            result = process(mStart +1, kSteps-1, nAll, pAim, dp)
                    +  process( mStart -1, kSteps-1, nAll, pAim, dp);
        }
        dp[mStart][kSteps] = result;

        return result;
    }

    public static void main(String[] args) {
        System.out.println(way1(2,5,6,5));
    }
}

纯动态规划方式

1. 根据递归的方式,我们知道,如果剩余步数为0,那么开始位置如果正好在目标位置处,就算是1种走法。并且可以推导二维数组的第一列,第五行为1,其他都为0

if (kSteps == 0) {
   return mStart == pAim ? 1 : 0;
}
xxxxxx
0
0
0
0
1
0

2. 根据递归方式,如果在最左端,只能往右走; 如果在最右端,只能往左走。目前第一列数据已经推导出来了。因此需要根据第一列进行推导。

//只能往右走
if (mStart == 1) {
    return process(mStart + 1, kSteps-1, nAll, pAim);
}
//只能往左走
if (mStart == nAll) {
    return process(nAll-1, kSteps-1, nAll, pAim);
}

根据递归代码,我们知道,无论是往左走,还是往右走。

第二行第二列,也就是dp[1][1],  都是下一行的前一列数据. 而最后一行最后一列,都是上一行前一列的数据。推导结果》

xxxxxx
00
0
0
0
1
01

3. 根据递归方法中,如果在中间位置。需要得到上一行和下一行的前一列累加值

//中间位置,即可往左,也可往右,需要统计出往左往右的和
return process(mStart +1, kSteps-1, nAll, pAim)
        +  process( mStart -1, kSteps-1, nAll, pAim);

推导出结果

xxxxxx
00
00
00
01
10
01

依次类推,得到的最终值为:

剩余0剩余1剩余2剩余3剩余4剩余5
目标位置0xxxxxx
目标位置1000010
目标位置2000105
目标位置3001040
目标位置4010309
目标位置5102050
目标位置6010209

根据题目我们知道,位置是从1到N的,也就是说位置不存在0的情况。而我们这张表位置作为行,剩余步数作为列的。因此位置为0直接忽略。

假设一共有6个位置,从2位置出发,走5步,到达位置5,一共有多少种走法

dp[2][5]可得到结果为5,即5中种走法。

package code03.动态规划_07;

/**
 * 假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2
 * 开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)
 * 如果机器人来到1位置,那么下一步只能往右来到2位置;
 * 如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;
 * 如果机器人来到中间位置,那么下一步可以往左走或者往右走;
 * 规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
 * 给定四个参数 N、M、K、P,返回方法数。
 */
public class Robot_02_opt2 {
    /**
     *
     * @param mStart   M 开始位置
     * @param kSteps   K 步数
     * @param nAll     N 长度
     * @param pAim     P 目的地位置
     * @return
     */
    public static int way(int nAll, int mStart, int pAim, int kSteps)
    {
        if (nAll < 2 || mStart < 1 || mStart > nAll || pAim < 1 || pAim > nAll || kSteps < 1) {
            return -1;
        }

        /**
         * 位置是1-N, 每一个位置都存在不同的走法,以每个位置为行,那么行的范围就是1-N
         * 由于java数组下标从0开始,想要获取1-N范围,长度需要加 1
         *
         * 我们将剩余步数为列,这样每个位置剩余步数就可以很直观的观察到.
         * 同理,列的长度也需要加 1。
         *
         * 其实,此处列无所谓的,哪怕就是100也行。只不过如果是100,那将会推导出
         * 走100的情况,无谓浪费资源。 最好的方式就是根据实际的业务,你要我推导
         * 几步,我就推导几步,节约资源
         */
        int[][] dp = new int[nAll+1][kSteps+1];
        //剩余步数为0,推导出对应哪一行为1
        dp[pAim][0] = 1;
        //根据剩余步数,推导出每一个位置能够走到目标位置的结果
        for (int col = 1; col <= kSteps; col++)
        {
            //第2行的每一列值, 对应的都是第3行前一列值
            dp[1][col] = dp[2][col-1];

            //从第二行开始,倒数第二行执行完后结束
            for (int row = 2; row < nAll; row++)
            {
                //中间行,等于上一行和下一行的前一列的和
                dp[row][col] = dp[row-1][col-1] + dp[row+1][col-1];
            }
            //最后一行,每一列值都对应上一行的前一列的值
            dp[nAll][col] = dp[nAll-1][col-1];
        }

        //根据当前哪个位置,剩余步数。获取到目标位置的不同走法
        return dp[mStart][kSteps];
    }



    public static void main(String[] args) {
        System.out.println(way(6,2,5,5));
    }
}

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

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

相关文章

初级程序员如何快速晋升为技术大牛

[请搜索公众号“云智AI助手”、“云智博瑞”关注我们 │ 谢谢支持 ] Cloud wisdom, AI assistant 作为初级程序员&#xff0c;你是否常常遇到代码优Bug调试的难题&#xff1f;幸运的是&#xff0c;ChatGPT可以助你一臂之力。本文将通过多个实例展示&#xff0c;如何借ChatGPT的…

【微信支付】分享一个失败的项目

这个项目是去年做的&#xff0c;开始客户还在推广&#xff0c;几个月后发现服务器已经关掉了。这是一个发图片猜谜语的应用&#xff0c;用户猜对了分红包&#xff0c;所得奖金可以提现。开发的时候对需求都不太看好&#xff0c;觉得用户粘性太低了。今天就把所有的程序拿了出来…

[抢先看] 全平台数据 (数据库) 管理工具 DataCap 1.10.0

推荐一个基于 SpringBoot 开发的全平台数据 (数据库管理工具) 功能比较完善&#xff0c;建议下载使用: github.com/EdurtIO/datacap 目前已经支持 40 多种数据源。国内首个应用 ChatGPT 到数据管理系统中项目。 在 DataCap v1.10.0 中我们主要核心修改了数据编辑器&#xff0c;…

多线程处理有序集合

文章目录 前言一、多线程处理有序集合&#xff1f;总结 前言 通过多线程,处理数据是一个快速提高处理的手段,那么当用多线程处理的时候,如果遇到有序集合怎么办?例如: 我想爬取一本小说,那么爬取完成后,需要的是 一个有序的章节小说,而非混乱的 章节,如何做呢? 一、多线程处…

.Net8顶级技术:边界检查之IR解析(慎入)

前言 C#这种语言之所以号称安全的&#xff0c;面向对象的语言。这个安全两个字可不是瞎叫的哦。因为JIT会检查任何可能超出分配范围的数值&#xff0c;以便使其保持在安全边界内。这里有两个概念&#xff0c;其一边界检查&#xff0c;其二IR解析。后者的生成是前者的功能的保证…

音视频使用qt测试ffmpeg接口时无法运行

仅仅时把自己过程中遇到的稍微阻塞疑惑问题做出整理&#xff0c;疑惑的是拿到的ffmpeg包中没有dll文件&#xff0c;导致自己研究了一系列。 使用qt加载音视频ffmpeg对应的相关lib库&#xff0c;进行接口&#xff0c;源码的研究。 1&#xff1a;使用源码安装的方式获取相关的动…

【蓝桥杯省赛真题40】Scratch报数游戏 蓝桥杯少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

目录 scratch报数游戏 一、题目要求 编程实现 二、案例分析 1、角色分析

OKR是什么意思啊

一、OKR是什么意思&#xff1f; OKR是"Objective and Key Results"的缩写&#xff0c;即目标和关键结果。它是一种目标管理框架&#xff0c;旨在帮助组织和团队设定明确的目标&#xff0c;并通过关键结果来衡量和追踪目标的实现情况。 为了让大家快速了解什么是OKR…

基于变长频带选择的JPEG图像可逆数据隐藏-文献学习

论文学习 原文题目&#xff1a; Reversible Data Hiding of JPEG Image Based on Adaptive Frequency Band Length 发表期刊&#xff1a; TCSVT 2023&#xff08;中科院1区&#xff09; 作者&#xff1a; Ningxiong Mao, Hongjie He, Fan Chen, Yuan Yuan, Lingfeng Qu 摘要 J…

SolVES模型应用(基于多源环境QGIS\PostgreSQL\ARCGIS\MAXENT\R语言支持下模型应用)

生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福祉具有重大意义&#xff0c;且被视为连接社会与生态系统的桥梁。自从启动千年生态系统评估项目&#xff08;Millennium Ecosystem Asse…

又双叒叕入选!腾讯安全NDR连续四年获Gartner认可

近日&#xff0c;全球权威研究机构 Gartner发布了2023年《Emerging Tech: Security — Adoption Growth Insights for Network Detection and Response》&#xff08;《新兴技术&#xff1a;安全-网络检测与响应的采用增长洞察》&#xff09;&#xff0c;腾讯安全连续四年被列为…

内卷把同事逼成了“扫地僧”,把Git上所有面试题整理成足足24W字测试八股文

互联网大厂更多的是看重学历还是技术&#xff1f; 毫无疑问&#xff0c;是技术&#xff0c;技术水平相近的情况下&#xff0c;肯定学历高/好的会优先一点&#xff0c;这点大家肯定都理解。 说实话&#xff0c;学弟学妹们找工作难&#xff0c;作为面试官招人也难呀&#xff01…

使用laf云开发三分钟上线你自己的Midjourney

文章尾部有demo 江湖惯例&#xff1a;先来一波感谢&#xff0c;感谢laf&#xff0c;让我们可以不使用魔法、免费接入Midjourney&#xff0c;不了解laf的请猛戳 Laf介绍 一、写这篇博客的背景 laf官方最近发布了一个活动&#xff0c;活动链接&#xff0c;新手也可以接入哦&…

云渲染平台为什么越来越多的效果图公司开始使用?

随着3dmax版本的不断更迭&#xff0c;包括常用的V-Ray渲染器和Corona渲染器的不断更新&#xff0c;室内设计行业对于 效果图的渲染要求越来越高。而要求更高的渲染精度和更真实的渲染效果&#xff0c;所需要付出的代价则是不断增长的参数&#xff0c;这会使渲染一张效果图的时间…

chatgpt赋能Python-python_lamba

Python Lambda: 什么是Lambda表达式&#xff1f; Python是一种强大的编程语言&#xff0c;它支持许多编程范式&#xff0c;包括函数式编程。Lambda表达式是函数式编程的一个重要概念。许多人对Lambda表达式感到困惑&#xff0c;认为它们很难理解。本文将介绍Python的Lambda表达…

如何对Windows剪切板里的内容进行取证分析 Windows剪切板取证

前言 无论是在现实中对设备进行取证分析&#xff0c;还是在ctf中做取证类的题目&#xff0c;剪切板里的内容都需要去查看&#xff0c;以免遗漏什么重要信息 剪切板位置 剪切板是计算机操作系统提供的一个临时存储区域&#xff0c;用于在不同应用程序之间复制和粘贴文本、图像…

S72402-NANANA伺服驱动器相当于大脑,执行电机相当于手脚

​ S72402-NANANA伺服驱动器相当于大脑&#xff0c;执行电机相当于手脚 伺服驱动器在控制信号的作用下驱动执行电机&#xff0c;因此驱动器是否能正常工作直接影响设备的整体性能。在伺服控制系统中&#xff0c;伺服驱动器相当于大脑&#xff0c;执行电机相当于手脚。而伺服驱动…

从裸机启动开始运行一个C++程序(三)

先序文章请看 从裸机启动开始运行一个C程序&#xff08;二&#xff09; 从裸机启动开始运行一个C程序&#xff08;一&#xff09; 编写MBR 上一章我们已经成功地在8086上运行了指令&#xff0c;同时也介绍了nasm汇编语言。那么接下来这一章&#xff0c;我们就来看看如何写BIO…

零基础学习网络安全这一块,请问有哪些相关资料可以推荐一下?

一、相关网站推荐 1、FreeBuf 国内关注度最高的全球互联网安全媒体平台&#xff0c;爱好者们交流与分享安全技术的社区&#xff0c;网络安全行业门户。 2、看雪看雪论坛是个软件安全技术交流场所&#xff0c;为安全技术爱好者提供一个技术交流平台和资源。 3、吾爱破解 吾爱破解…

Spring源码阅读:Spring事务传播特性

一、概述 我们平常工作中经常会听到事务的传播级别&#xff0c;但是使用中基本不会太调整这个事务传播级别&#xff0c;因为没什么业务场景需要我们这么做&#xff0c;只需要使用原有的事务传播级别即可解决95%的业务场景。但是为了那5%的业务场景&#xff0c;我们还是还要学习…