有趣的算法题之机器人

小码哥与机器人

小码哥新买了一个机器人,但是这个机器人因为很便宜只能做三个动作。

三个动作: 前进 FD ,倒退 BK 和重复 REPEAT 。
FD 后加数字表示前进多少步;
BK 后加数字表示后退多少步;
REPEAT 后加数字再加方括号,表示重复方括号里的命令。
三个动作加的数字均为正整数。

命令的格式:
1. FD 与 BK 命令组合;
2. REPEAT 命令内加 REPEAT 命令与 FD 、BK 组合,且 REPEAT 排在最后。

原题链接:码题集OJ-小码哥与机器人 (matiji.net)

格式

输入格式:一行字符串(长度不超过 200 )。

输出格式:表示最后得到的步数,表示离起点的距离(非负整数)。

样例 

输入:REPEAT 5[ FD 50 REPEAT 10[FD 100]]

输出:5250

问题分析

要计算机器人在给定命令序列下的最终位置,我们需要按照命令的格式进行逐步解析。

假设输入的命令字符串是:FD 5 BK 3 REPEAT 2[FD 2 BK 1]

解析 FD 和 BK 命令

        首先,机器人执行 FD 5 ,即前进 5 步。然后,机器人执行 BK 3 ,即后退 3 步。此时,机器人离起点的距离是 5 步(前进的步数)减去 3 步(后退的步数),等于 2 步。我们可以发现,单纯 FD 和 BK 命令的组合时,我们只要简单地对步数想加减就行。

解析 REPEAT 命令

        接下来,机器人遇到 REPEAT 2[FD 2 BK 1 ]。这意味着机器人需要重复方括号内的命令两次。

        在重复命令中,机器人执行 FD 2 ,前进 2 步,执行 BK 1 ,后退 1 步。因此该重复命令一次执行的步数为 前进 1 步,我们在计算后需要存储该值。由于执行了两次,因此我们也需要对该命令的重复步数进行存储。

        接着,我们再考虑 REPEAT 命令嵌套 REPEAT 命令的情况,我们可以发现简单地设置变量来存储重复命令的执行已经难以完成。因此,根据 REPEAT 命令的执行过程,我们可以采用递归的方式来进行模拟。这样在递归过程中的方法栈中,我们可以存储重复命令执行一次的步数以及重复命令的重复次数,之后再计算完后进行提交到上一个重复命令处即可。

计算总步数

        将 REPEAT 命令内的步数累加到之前的步数上。由于 REPEAT 命令执行了两次,并且每次重复内部命令导致机器人前进了 1 步,因此总步数增加了 2 步。加上之前的 2 步,机器人总共前进了 4 步。因此,最终机器人离起点的距离是 4 步。

代码

import java.util.Scanner;
import java.util.*;

class Main {
   static char[] cs; // 命令字符数组
   static int i = 0; // 命令位置索引
   
   // 主函数
   public static void main(String[] args) {
      Scanner input = new Scanner(System.in);
      String s = input.nextLine(); // 输入命令
      cs = s.toCharArray(); // 将字符串转为字符数组
      long ans = Math.abs(digui()); // 递归获取结果,对答案进行取绝对值
      System.out.println(ans);
      input.close();
   }

    // 递归计算
   public static long digui() {
      long num = 1; // 该层递归的乘数因子
      long cout = 0; // 该层命令行走的距离
      boolean isPost = true; // 确认命令方向的正负
      while (i < cs.length) {
         if (cs[i] == ' ') { // 当字符为空时跳过
            i++;
         }else if (cs[i] == 'F') { // 当命令为正向时,修改方向
            i+= 3; // 跳到数字所在的位置
            isPost = true; // 修改方向为正
         }else if (cs[i] == 'B') { // 当命令为负向时,修改方向
            i+= 3; // 跳到数字所在的位置
            isPost = false; // 修改方向为负
         }else if (cs[i] == 'R') { // 当命令为重复时,获取该命令要重复几次
            i+= 7; // 跳到数字所在位置
            num = getNum(); // 获取命令重复数字
         }else if (cs[i] == '[') { // 为左括号时,我们需要进入下一层递归来计算这个重复命令的步数和
            i++; // 跳过这个符号
            long res = digui(); // 进入下一层递归,获取该重复命令的步数和
            cout+= num*res; // 计算这个重复命令的总步数和
         }else if (cs[i] == ']') { // 为右括号时,说明该重复命令走完了,将步数和提交给上一层
            i++; // 跳过这个符号
            return cout; // 提交步数和
         }else if (cs[i] >= '0' && cs[i] <= '9') { // 当为数字时,获取整个数字
            if (isPost) { // 为正向时就加
                cout+= getNum();
            }else { // 为负向时则减
                cout-= getNum();
            }
         }
      }
      return cout; // 如果整个命令没有出现重复命令,则计算完后提交
   }

   // 获取整个数字的函数
   public static long getNum() {
      if (cs[i] >= '0' && cs[i] <= '9') {
         long num = cs[i] - '0'; // 取出该位数字
         // 如果后面还有位数,我们就将数字乘十,提高数字的位数
         while (i+1 < cs.length && cs[i+1] >= '0' && cs[i+1] <= '9') { 
            num*= 10;
            i++;
            num+= cs[i] - '0';
         }
         i++; // 已经计算完数字,跳过数字的末尾
         return num; // 返回获取到的数字
      }else {
         return 0; // 如果不是数字则返回 1,在重复命令获取乘数因子时可能出现空格符号
      }
   }
}

最后


 如果有所收获请点赞支持一下作者哦,您的点赞是我持续创作的动力!! 

  • 43
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值