机器人到达指定位置的方法数问题

机器人,到达,指定,位置,方法,问题 · 浏览次数 : 286

小编点评

**机器人到达指定位置的方法数** **算法:** 1. **暴力解**:递归地遍历所有可能的机器人移动路径,并计算每一条路径的方法数。 2. **动态规划**:利用二维数组 **dp**存储已计算的路径数量,以减少重复计算。 3. **空间压缩**:使用一个一维数组存储 **dp**,以减少空间复杂度。 **详细步骤:** **暴力解** 1. **初始化**: - 设置 **dp**数组,其中 **dp[i][j]** 表示在 **len** 个位置上从 **i** 个位置移动到 **j** 个位置的方法数。 - **dp[end][0]** 设置为 1,因为从任何位置都可以到达目标位置。 2. **循环遍历**: - 遍历所有可能的 **j** 值(从 1 到 **step**)。 - 对于每个 **j** 值,遍历所有可能的 **i** 值(从 0 到 **len**)。 - 计算在 **i** 个位置从 **j** 个位置移动到 **j** 个位置的方法数。 - 更新 **dp[i][j]**,表示从 **i** 个位置移动到 **j** 个位置的方法数。 **动态规划** 1. **创建**一个二维数组 **dp**,其中 **dp[i][j]** 表示在 **len** 个位置上从 **i** 个位置移动到 **j** 个位置的方法数。 2. **初始化**: - 设置 **dp[end][0]** 为 1。 - 设置 **dp[i][j]** 为 0,其中 **i > 0** 和 **j > 0**。 3. **循环计算**: - 对于每个 **j** 值(从 1 到 **step**),循环遍历所有 **i** 值(从 1 到 **len**)。 - 计算在 **i** 个位置从 **j** 个位置移动到 **j** 个位置的方法数。 - 将这些方法数累加到 **dp[i][j]** 中。 **空间压缩** 1. **创建**一个长度为 **len** 的数组 **dp**。 2. **初始化**: - 设置 **dp[end][0]** 为 1。 - 设置 **dp[i][j]** 为 0,其中 **i > 0** 和 **j > 0**。 3. **循环计算**: - 对于每个 **j** 值(从 1 到 **step**),循环遍历所有 **i** 值(从 1 到 **len**)。 - 计算在 **i** 个位置从 **j** 个位置移动到 **j** 个位置的方法数。 - 将这些方法数累加到 **dp[i][j]** 中。 **结果** **暴力解**:** 方法数 **O(n * m)**,其中 **n** 是 **len**,**m** 是 **step**。 **动态规划**:** 方法数 **O(n * m)**。 **空间压缩**:** 方法数 **O(n)**。

正文

机器人到达指定位置的方法数问题

作者:Grey

原文地址:

博客园:机器人到达指定位置的方法数问题

CSDN:机器人到达指定位置的方法数问题

题目描述

链接:https://www.nowcoder.com/questionTerminal/54679e44604f44d48d1bcadb1fe6eb61
来源:牛客网

假设有排成一行的N个位置,记为1~N,开始时机器人在M位置,机器人可以往左或者往右走,如果机器人在1位置,那么下一步机器人只能走到2位置,如果机器人在N位置,那么下一步机器人只能走到N-1位置。规定机器人只能走k步,最终能来到P位置的方法有多少种。由于方案数可能比较大,所以答案需要对1e9+7取模。

暴力解

定义递归函数

long ways(int len, int start, int step, int end)

递归含义表示:机器人从坐标 start 开始,只能走 step 步,到达 end 的方法数是多少

接下来是 base case,

if (step == 0) {
    if (start == end) {
        return 1L;
    }
    return 0L;
}

如果 step 只剩下 0 步,说明没有步数可以走了,此时,如果 start == end ,表示正好就在目的地,返回一种方法数;

否则,返回 0 种方法数。

接下来是普遍情况,如果 start == 0,只能向右边走,即: ways(len, start + 1, step - 1, end)

如果 start == len - 1,只能向左边走,即:ways(len, start - 1, step - 1, end)

不在两端位置,则既可以向左边走,也可以向右边走,即:(ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end))

暴力解法完整代码如下

    public static long ways(int len, int start, int step, int end) {
        if (step == 0) {
            if (start == end) {
                return 1L;
            }
            return 0L;
        }
        // step不止一步
        if (start == 0) {
            return ways(len, start + 1, step - 1, end);
        } else if (start == len - 1) {
            return ways(len, start - 1, step - 1, end);
        } else {
            return (ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end));
        }
    }

超时

image

动态规划-缓存法(可 AC)

上述暴力递归过程,可变参数有两个 start,step;可以设置一个二维数组 dp,用于缓存递归中间过程的解,

long[][] dp = new long[len][step + 1];

dp[i][j]表示ways(len,i,j,end)的值,dp数组的值均初始化为 -1。

上述暴力递归过程增加这个 dp 参数,如果dp[i][j] != -1,则说明已经算过这个递归过程,直接返回dp[i][j]的值即可。

完整代码如下

    public static long ways(int len, int start, int step, int end) {
        long[][] dp = new long[len][step + 1];
        for (int i = 0; i < len; i++) {
            for (int j = 0; j <= step; j++) {
                dp[i][j] = -1L;
            }
        }
        dp(len, start, step, end, dp);
        return dp[start][step];
    }

    private static long dp(int len, int start, int step, int end, long[][] dp) {
        if (dp[start][step] != -1L) {
            return dp[start][step] % MOD;
        }
        if (step == 0) {
            dp[start][step] = start == end ? 1L : 0L;
            return dp[start][step];
        }
        long ans;
        // step不止一步
        if (start == 0) {
            ans = dp(len, start + 1, step - 1, end, dp);
        } else if (start == len - 1) {
            ans = dp(len, start - 1, step - 1, end, dp);
        } else {
            ans = (dp(len, start - 1, step - 1, end, dp) + dp(len, start + 1, step - 1, end, dp));
        }
        dp[start][step] = ans;
        return ans;
    }

动态规划解-严格位置依赖(可 AC)

回到暴力递归解,伪代码如下

    public static long ways(int len, int start, int step, int end) {
        ……
        if (start == 0) {
            return ways(len, start + 1, step - 1, end);
        } else if (start == len - 1) {
            return ways(len, start - 1, step - 1, end);
        } else {
            return (ways(len, start - 1, step - 1, end) + ways(len, start + 1, step - 1, end));
        }
    }

根据缓存法得知,该递归过程使用一个二维数组 dp 即可存下所有结果,其中

dp[i][j]表示ways(len,i,j,end)的值,

通过观察上述暴力递归过程,dp[i][j]依赖的位置是dp[i+1][j-1]dp[i-1][j-1]

所以,依据上述递归过程,可以改成严格位置的动态规划版本,完整代码如下

    public static long ways(int len, int start, int step, int end) {
        long[][] dp = new long[len][step + 1];
        // 填好第0列
        dp[end][0] = 1L;
        for (int j = 1; j < step + 1; j++) {
            for (int i = 0; i < len; i++) {
                if (i == 0) {
                    dp[i][j] = dp[1][j - 1];
                } else if (i == len - 1) {
                    dp[i][j] = dp[len - 2][j - 1];
                } else {
                    dp[i][j] = dp[i - 1][j - 1] % MOD + dp[i + 1][j - 1] % MOD;
                }
            }
        }
        return dp[start][step];
    }

动态规划解-空间压缩版(可 AC)

通过上述严格位置的动态规划版本可以得知,dp[i][j]位置,依赖的位置是dp[i+1][j-1]dp[i-1][j-1],

示例图如下

image

而且,通过上述动态规划解,可以得知第 0 列中,除了dp[end][0] = 1L,其余都是 0,所以 dp 的第0列可以直接填充

image

所以,只需要用一个一维数组表示第 0 列,然后根据依赖关系,通过第 0 列推出第一列的值,一维数组此时表示第一列的值,依次这样递推下去,一直到最后一列,得解,这种方法就可以将二维数组压缩成一维数组,节省了空间复杂度。

完整代码如下

    public static long ways(int len, int start, int step, int end) {
        long[] dp = new long[len];
        dp[end] = 1L;
        long tmp = 0;
        for (int j = 1; j < step + 1; j++) {
            for (int i = 0; i < len; i++) {
                long ways = dp[i];
                if (i == 0) {
                    dp[i] = dp[1] % MOD;
                } else if (i == len - 1) {
                    dp[i] = tmp % MOD;
                } else {
                    dp[i] = tmp % MOD + dp[i + 1] % MOD;
                }
                tmp = ways;
            }
        }
        return dp[start];
    }

更多

算法和数据结构笔记

与机器人到达指定位置的方法数问题相似的内容:

机器人到达指定位置的方法数问题

机器人到达指定位置的方法数问题 作者:Grey 原文地址: 博客园:机器人到达指定位置的方法数问题 CSDN:机器人到达指定位置的方法数问题 题目描述 链接:https://www.nowcoder.com/questionTerminal/54679e44604f44d48d1bcadb1fe6e

机器学习策略:详解什么时候该改变开发/测试集和指标?(When to change dev/test sets and metrics)

什么时候该改变开发/测试集和指标? 有时候在项目进行途中,可能意识到,目标的位置放错了。这种情况下,应该移动的目标。 来看一个例子,假设在构建一个猫分类器,试图找到很多猫的照片,向的爱猫人士用户展示,决定使用的指标是分类错误率。所以算法\(A\)和\(B\)分别有3%错误率和5%错误率,所以算法\(

[转帖]机械磁盘读取数据浅析

https://cdn.modb.pro/db/523794 读取硬盘上的数据,第一步就是找到所需的磁道,磁道就是以中间轴为圆心的圆环,首先我们需要找到所需要对准的磁道,并将磁头移动到对应的磁道上,这个过程叫做寻道。然后,我们需要等到磁盘转动,让磁头指向我们需要读取的数据开始的位置,这里耗费的时间称

Mac上使用Royal TSX快速连接到OCI主机

**问题:** 每次使用Royal TSX连接到OCI主机都要交互式输入opc这个用户名,次数多了也蛮烦。 那如何既指定用户名,又想要通过ssh私钥登陆机器呢? 这个需求确实很初级,但也着实困扰过我,因为开始我真的以为不支持,认为这两种连接方式只能选其一。结果没想到人家是可以组合使用实现这样的需求。

[机器学习] 低代码机器学习工具PyCaret库使用指北

PyCaret是一个开源、低代码Python机器学习库,能够自动化机器学习工作流程。它是一个端到端的机器学习和模型管理工具,极大地加快了实验周期,提高了工作效率。PyCaret本质上是围绕几个机器学习库和框架(如scikit-learn、XGBoost、LightGBM、CatBoost、spaCy

从k8s 的声明式API 到 GPT的 提示语

命令式命令式有时也称为指令式,命令式的场景下,计算机只会机械的完成指定的命令操作,执行的结果就取决于执行的命令是否正确。GPT 之前的人工智能就是这种典型的命令式,通过不断的炼丹,告诉计算机要怎么做,计算机只是机械的完成指定场景下的任务。声明式声明式也称为描述式或者申明式,这种方式告诉计算机想要的,

LAMP-CentOS7搭建Web服务器

搭建LAMP Web服务器 在家中翻到了以前用的老电脑,在思索一番后,决定把这台电脑改造成一台Web服务器,作为我自己搭建博客的测试机器。 一、Linux服务器 LAMP中的L指的是Linux服务器,其中Linux服务器的版本众多,如,CentOS、Ubuntu等Linux版本,我自己选择了Cent

10.5 认识XEDParse汇编引擎

XEDParse 是一款开源的x86指令编码库,该库用于将MASM语法的汇编指令级转换为对等的机器码,并以XED格式输出,目前该库支持x86、x64平台下的汇编编码,XEDParse的特点是高效、准确、易于使用,它可以良好地处理各种类型的指令,从而更容易地确定一段程序的指令集。XEDParse库可以集成到许多不同的应用程序和工具中,因此被广泛应用于反汇编、逆向工程、漏洞分析和入侵检测等领域。XED

【numpy基础】--广播计算

`numpy`的广播计算是指在多维数组上进行的一种高效计算方式。 它可以将计算任务分配到每个维度上,并且可以在计算过程中进行数据共享和同步,从而提高计算效率和精度。 广播计算在数值计算、科学计算、机器学习等领域都有广泛的应用。 例如,在数值计算中,广播计算可以用于求解大规模的非线性方程组;在科学计算

驱动开发:内核RIP劫持实现DLL注入

本章将探索内核级DLL模块注入实现原理,DLL模块注入在应用层中通常会使用`CreateRemoteThread`直接开启远程线程执行即可,驱动级别的注入有多种实现原理,而其中最简单的一种实现方式则是通过劫持EIP的方式实现,其实现原理可总结为,挂起目标进程,停止目标进程EIP的变换,在目标进程开启空间,并把相关的指令机器码和数据拷贝到里面去,然后直接修改目标进程EIP使其强行跳转到我们拷贝进去的