机器人的运动范围-Python实现
个人微信公众号:AI研习图书馆,欢迎关注~
深度学习知识及资源分享,学习交流,共同进步~
一、题目描述
面试题13:机器人的运动范围
难度:中等
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例2
输入:m = 3, n = 1, k = 0
输出:1
提示:
1 <= n,m <= 100,0 <= k <= 20
二、解题实现
1. 解题思路
本题与 矩阵中的路径 类似,是典型的矩阵搜索问题。此类问题通常可使用 深度优先搜索(DFS) 或 广度优先搜索(BFS) 解决。在介绍 DFS / BFS 算法之前,为提升计算效率,首先讲述两项前置工作: 数位之和计算 、搜索方向简化 。
数位之和计算:
def sums(x):
s = 0
while x:
s += x % 10
x = x // 10
return s
以下代码为增量公式的三元表达式写法,将整合入最终代码中。
s_x + 1 if (x + 1) % 10 else s_x - 8
搜索方向简化:
-
数位和特点: 根据数位和增量公式得知,数位和每逢 进位 突变一次。
-
解的三角形结构:
-
根据数位和特点,矩阵中 满足数位和的解 构成的几何形状形如多个 等腰直角三角形 ,每个三角形的直角顶点位于 0, 10, 20, …0,10,20,… 等数位和突变的矩阵索引处 。
-
三角形内的解虽然都满足数位和要求,但由于机器人每步只能走一个单元格,而三角形间不一定是连通的,因此机器人不一定能到达,称之为 不可达解 ;同理,可到达的解称为 可达解 (本题求此解) 。
-
结论: 根据可达解的结构,易推出机器人可 仅通过向右和向下移动,访问所有可达解 。
-
三角形内部: 全部连通,易证;
-
两三角形连通处: 若某三角形内的解为可达解,则必与其左边或上边的三角形连通(即相交),即机器人必可从左边或上边走进此三角形。
图例展示了 n,m = 20 ,k∈[6,19] 的可达解、不可达解、非解,以及连通性的变化。
动态动画请参看以下链接:
动态演示
2. Python实现
2.1 方法一:深度优先遍历 DFS
深度优先搜索一般使用栈来实现。本题使用递归可以更轻松实现,我们定义一个递归函数 dfs(),如果坐标不满足条件,结束递归状态,否则将下一步满足条件的坐标代入递归函数。
python代码实现:
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def sumofDigit(x, y):
result = 0
while x > 0:
result += x % 10
x //= 10
while y > 0:
result += y % 10
y //= 10
return result
def dfs(i, j):
if i == m or j == n or sumofDigit(i, j) > k or (i, j) in marked:
return
marked.add((i, j))
dfs(i + 1, j)
dfs(i, j + 1)
marked = set()
dfs(0, 0)
return len(marked)
复杂度分析:
- 时间复杂度:O(m×n)。
- 空间复杂度:O(m×n)。
2.2 方法二:广度优先搜索 BFS
广度优先搜索一般使用队列来实现。我们先将 (0, 0)加入队列,当队列不为空时,每次将队首坐标出队,加入到集合中,再将满足以上两个条件的坐标加入到队尾,直到队列为空。
Python代码实现:
在广度优先算法,由于可行解的连通性和结构,仅考虑向下和向右的移动方向即可,深度优先算法同样如此。
由于题目要求从 (0, 0) 点出发,因此任何一个点都可以只通过向右和向下两个动作达到,因此代码中可以只考虑这两个方向。
class Solution:
def sum_rc(self,row,col):
tmp = 0
while row > 0:
tmp += row % 10
row //= 10
while col > 0:
tmp += col % 10
col //= 10
return tmp
def movingCount(self, m: int, n: int, k: int) -> int:
marked = set() # 将访问过的点添加到集合marked中,从(0,0)开始
queue = collections.deque()
queue.append((0,0))
while queue:
x, y = queue.popleft()
if (x,y) not in marked and self.sum_rc(x,y) <= k:
marked.add((x,y))
for dx, dy in [(1,0),(0,1)]: # 仅考虑向右和向下即可
if 0 <= x + dx < m and 0 <= y + dy < n:
queue.append((x+dx,y+dy))
return len(marked)
复杂度分析:
- 时间复杂度:O(m\times n)O(m×n)
- 空间复杂度:O(m\times n)O(m×n)
码字不易,欢迎大家点赞,关注,精彩不断~
您的支持,是我不断创作的最大动力~
欢迎点赞,关注,留言交流~
深度学习,乐此不疲~