回溯算法-机器人的运动范围

开发 算法
有一个矩阵,机器人可以从坐标(0,0)的格子开始移动,它每次可以向左、右、上、下移动一格,但是不能进入行坐标和列坐标的数位之和大于K的格子,求这个机器人总共能走多少个格子以及它的行动轨迹。

[[415476]]

本文转载自微信公众号「神奇的程序员K」,作者神奇的程序员K。转载本文请联系神奇的程序员K公众号。

前言

有一个矩阵,机器人可以从坐标(0,0)的格子开始移动,它每次可以向左、右、上、下移动一格,但是不能进入行坐标和列坐标的数位之和大于K的格子,求这个机器人总共能走多少个格子以及它的行动轨迹。

本文就跟大家分享下这个问题的解决方案 ,欢迎各位感兴趣的开发者阅读本文。

实现思路

在上一篇讲解寻找矩阵中的路径文章中,我们学会了使用回溯算法来访问矩阵中的格子,本文要讨论的这个问题在访问格子之前做了一层判断,如果满足条件就能进入,不满足就无法进入。

我们要做的这层判断为:计算出待访问格子的坐标的数位之和,如果其大于K(最大活动范围)则不能访问。

数位之和:即取出数字中每个位置的值,将其相加得出的结果。例如:19的数位之和就是1 + 9 = 10。

判断当前格子是否已访问

首先,我们需要创建一个与原矩阵大小相同的矩阵,用于标识机器人是否已走这个格子。

在js中无法直接创建指定大小的二维数组,创建思路如下:

  • 以矩阵的长度为大小创建一个数组
  • 遍历创建好的数组,再以矩阵的第0号数组的长度为大小创建数组,赋值给遍历到的每一项。

判断格子是否可进入

在访问格子时,我们需要判断下要访问的格子是否能进入,我们需要计算出行坐标与列坐标的数位之和,然后将其相加,判断相加后的结果是否大于机器人的最大活动范围(K)。

计算数位之和有两种做法:

  • 将数字转为字符串,遍历取出每个字符将其转为数字后再相加
  • 对数字进行模运算,将其结果相加,再对数字本身进行/10操作,直至数字小于等于0

开始移动机器人

移动机器人时,我们需要7个参数:

  • 矩阵的总行数
  • 矩阵的总列数
  • 即将进入格子的行坐标
  • 即将进入格子的列坐标
  • 最大活动范围
  • 访问标识矩阵
  • 路径矩阵

首先,我们需要进行边界条件判断(递归的终止条件),条件满足代表该格子无法访问,可行走格子为0(直接返回0):

  • 待访问格子的行坐标大于矩阵的总行数
  • 待访问格子的行坐标小于0
  • 待访问格子的列坐标大于矩阵的总列数
  • 待访问格子的列坐标小于0
  • 当前格子已经被访问
  • 当前格子不能进入

如果上述条件都满足则表示当前格子可以访问,保存当前格子中的值到行动轨迹中,标识当前格子为已访问状态,已行走格子数+1,并递归尝试当前格子的其它四个方向的格子能否进入。

当递归栈清空后,我们也就得到了机器人总共可以进入的格子总数以及它的行动轨迹。

实现代码

接下来,我们将上述思路转换为TypeScript代码。

格子能否进入函数

我们先来看下判断当前格子能否进入的函数实现,如下所示:

  1.   /** 
  2.    * 判断机器人能否进入目标格子 
  3.    * @param row 行坐标 
  4.    * @param col 列坐标 
  5.    * @param target 临界点 
  6.    * @private 
  7.    */ 
  8.   private checkPath(row: number, col: number, target: number): boolean { 
  9.     // 两坐标的数位之和必须小于等于临界点 
  10.     return sumOfDigits(row) + sumOfDigits(col) <= target; 
  11.   } 
  12.  
  13. // 转字符串实现 
  14. export function sumOfDigits(target: number): number { 
  15.   let finalVal = 0; 
  16.   const computeVal = target.toString(); 
  17.   for (let i = 0; i < computeVal.length; i++) { 
  18.     finalVal += Number(computeVal[i]); 
  19.   } 
  20.   return finalVal; 
  21.  
  22. // 数位之和 - 模运算实现 
  23. export function sumOfDigitsForModular(target: number): number { 
  24.   let finalVal = 0; 
  25.   while (target > 0) { 
  26.     finalVal += Math.floor(target % 10); 
  27.     target /= 10; 
  28.   } 
  29.   return finalVal; 

移动机器人函数

移动机器人至指定格子实现代码如下所示:

  1. /** 
  2.  * 开始移动机器人 
  3.  * @param rows 矩阵总行数 
  4.  * @param cols 矩阵总列数 
  5.  * @param row 待进入格子的行坐标 
  6.  * @param col 待进入格子的列坐标 
  7.  * @param threshold 最大活动范围 
  8.  * @param isVisited 访问标识矩阵 
  9.  * @param matrix 路径矩阵 
  10.  * @private 
  11.  */   
  12. rivate startMoving( 
  13.   rows: number, 
  14.   cols: number, 
  15.   row: number, 
  16.   col: number, 
  17.   threshold: number, 
  18.   isVisited: Array<Array<boolean>>, 
  19.   matrix: Array<Array<T>> 
  20. ): number { 
  21.   // 边界条件判断 
  22.   if ( 
  23.     row >= rows || 
  24.     row < 0 || 
  25.     col >= cols || 
  26.     col < 0 || 
  27.     isVisited[row][col] || 
  28.     !this.checkPath(row, col, threshold) 
  29.   ) { 
  30.     return 0; 
  31.   } 
  32.   // 记录当前访问的格子内容 
  33.   this.path += `${matrix[row][col]} -> `; 
  34.   // 标识当前格子已被访问 
  35.   isVisited[row][col] = true
  36.   // 格子访问数量+1 
  37.   return ( 
  38.     1 + 
  39.     this.startMoving(rows, cols, row + 1, col, threshold, isVisited, matrix) + 
  40.     this.startMoving(rows, cols, row, col + 1, threshold, isVisited, matrix) + 
  41.     this.startMoving(rows, cols, row - 1, col, threshold, isVisited, matrix) + 
  42.     this.startMoving(rows, cols, row, col - 1, threshold, isVisited, matrix) 
  43.   ); 

主函数

最后,我们来看下主函数的实现,如下所示:

  1. /** 
  2.  * 题目: 
  3.  * 地上有一个m行n列的方格。 
  4.  * 一个机器人从坐标(0,0)的格子开始移动, 
  5.  * 它每次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。 
  6.  * 例如,当k为18时,机器人能够进入方格 (35,37),因为3+5+3+7=18。 
  7.  * 但它不能进入方格(35,38),因为3+5+3+8=19. 请问该机器人能够到达多少个格子? 
  8.  * @param matrix 矩阵 
  9.  * @param threshold 临界点(最大活动范围) 
  10.  */ 
  11. public movingCount(matrix: Array<Array<T>>, threshold = 0): number { 
  12.   if (threshold < 0 || matrix.length <= 0) { 
  13.     return 0; 
  14.   } 
  15.   // 获取方格的总行数与总列数 
  16.   const rows = matrix.length; 
  17.   const cols = matrix[0].length; 
  18.   // 创建标记数组,用于标识格子是否被访问 
  19.   const isVisited: Array<Array<boolean>> = new Array(rows); 
  20.   for (let i = 0; i < isVisited.length; i++) { 
  21.     isVisited[i] = new Array(cols); 
  22.   } 
  23.   // 从0,0位置开始移动,计算总的移动格子数量 
  24.   return this.startMoving(rows, cols, 0, 0, threshold, isVisited, matrix); 

完整代码请移步:Backtracking.ts#L80

编写测试用例

接下来,我们构造一个矩阵来验证下上述代码能否正确执行,如下所示:

  1. const pathArr = [ 
  2.   ["a""b""t""g"], 
  3.   ["c""f""c""s"], 
  4.   ["j""d""e""h"
  5. ]; 
  6.  
  7. const backtracking = new Backtracking<string>(); 
  8. const totalCount = backtracking.movingCount(pathArr, 4); 
  9. const path = backtracking.path; 
  10. console.log( 
  11.   "机器人总共可走的格子总数为: "
  12.   totalCount, 
  13.   ",运动轨迹为: "
  14.   path.substr(0, path.length - 3) 
  15. ); 

执行结果如下所示:

 

责任编辑:武晓燕 来源: 神奇的程序员K
相关推荐

2021-10-20 10:48:30

机器人人工智能算法

2019-06-26 23:27:33

机器人物联网应用IOT

2020-10-15 15:42:00

人工智能

2020-09-24 10:49:50

机器人人工智能系统

2015-12-10 21:49:32

IM机器人

2021-07-22 10:17:55

加密机器人加密货币机器人

2015-07-28 09:36:11

机器人

2021-08-19 15:44:20

机器人人工智能机器学习

2022-07-28 11:26:41

人工智能机器人

2017-03-28 12:21:21

机器人定义

2012-03-08 09:42:16

开源软件Linux

2021-03-25 09:25:55

机器人人工智能系统

2020-04-09 09:56:55

机器人导航框架

2021-04-08 09:33:02

机器人物联网技术物联网

2019-11-06 11:40:19

机器人人工智能系统

2018-07-05 17:01:42

人工智能机器学习机器人

2021-08-21 15:35:30

机器人人工智能算法

2021-07-19 09:11:05

机器人人工智能算法

2019-03-05 10:38:44

机器人人工智能系统
点赞
收藏

51CTO技术栈公众号