提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、题目
地上有一个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。请问该机器人能够到达多少个格子?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
输入:m = 2, n = 3, k = 1
输出:3
二、使用步骤
1.1 深度优先—递归
1.1.1 解决思路
- 当前问题
判断从(i,j) 出发,到(m-1,n-1)可以经过多少不重复的格子 - 划分为子问题
(i+1,j)-------> (m-1,n-1)
(i-1,j)-------> (m-1,n-1)
(i,j+1)-------> (m-1,n-1)
(i,j-1)-------> (m-1,n-1)
即,从(i,j)开始为4个子问题相加+1(当前 i,j 格子) - 判断终止条件及剪枝
① i,j越界
② 不重复的格子
设置 v i s t e d [ i ] [ j ] = T r u e / F a l s e visted[i][j]=True/False visted[i][j]=True/False , 判断其是否已经被访问过
③ 数位和 > k
1.1.2 具体代码
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
#回溯法
def digitalSum(s):
res=0
while s:
res+=s%10
s=s//10
return res
def backstacking(i,j,k):
if i<0 or i>=m or j<0 or j>=n:
return 0
#如果行列坐标位数之后大于k,不能进入
if visted[i][j]:
return 0
if digitalSum(i)+digitalSum(j)>k:
return 0
visted[i][j]=True
res=1+backstacking(i-1,j,k)+backstacking(i+1,j,k)+backstacking(i,j+1,k)+backstacking(i,j-1,k)
return res
visted=[[0 for i in range(n)] for j in range(m)]
return(backstacking(0,0,k))
时间复杂度:最坏情况下,需要访问整个(m-1,n-1). O(mn)
空间复杂度:维护了一个visted[][], O(mn)
1.1.3 改进
- 使用set()动态维护已经访问过的元素,节省空间
set1=set()
set1.add((i,j))
if (i,j) in set1:
return 0
- 从点(0,0)出发,仅向下向右就可以遍历所有解。
- 使用si,sj表示i,j数位和
当(i+1)%10==0时,s(i+1)=s(i)-8
i=9,i+1=10-----> si=9,s(i+1)=1,------>s(i+1)=s(i)-8
当(i+1)%10!=0时,s(i+1)=s(i)+1
i=8,i+1=9-----> si=8,s(i+1)=9,------>s(i+1)=s(i)+1
三元运算符表示为:
si+1 if (i+1)%10 else si-8
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
#回溯法
# def digitalSum(s):
# res=0
# while s:
# res+=s%10
# s=s//10
# return res
def backstacking(i,j,k,si,sj):
if i<0 or i>=m or j<0 or j>=n:
return 0
# if visted[i][j]:
# return 0
if (i,j) in set1:
return 0
#如果行列坐标位数之后大于k,不能进入
# if digitalSum(i)+digitalSum(j)>k:
# return 0
if si+sj>k:
return 0
#visted[i][j]=True
set1.add((i,j))
#仅向下向右就可以遍历所有解
res=1+backstacking(i+1,j,k,si+1 if (i+1)%10 else si-8,sj)+backstacking(i,j+1,k,si,sj+1 if (j+1)%10 else sj-8)
return res
#visted=[[0 for i in range(n)] for j in range(m)]
set1=set()
return(backstacking(0,0,k,0,0))
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def backstacking(i,j,k,si,sj):
if i<0 or i>=m or j<0 or j>=n:
return 0
if (i,j) in set1:
return 0
if si+sj>k:
return 0
set1.add((i,j))
#仅向下向右就可以遍历所有解
res=1+backstacking(i+1,j,k,si+1 if (i+1)%10 else si-8,sj)+backstacking(i,j+1,k,si,sj+1 if (j+1)%10 else sj-8)
return res
set1=set()
return(backstacking(0,0,k,0,0))
Krahets
https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/solution/mian-shi-ti-13-ji-qi-ren-de-yun-dong-fan-wei-dfs-b/
2.广度优先–队列
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def digitalSum(s):
res=0
while s:
res+=s%10
s=s//10
return res
q=collections.deque()
q.append((0,0))
s=set()
while q:
i,j=q.popleft()
if i>=0 and i<m and j>=0 and j<n and (i,j) not in s and digitalSum(i)+digitalSum(j)<=k:
s.add((i,j))
for l,r in [(i+1,j),[i,j+1]]:
q.append((l,r))
return len(s)