每日一道剑指offer-机器人的运动范围

题目:

地上有一个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

解析:

这道题一看题目就很明显的是路径搜索题,和《矩阵中的路径》这道题特别相似,都是在单元格中上下左右移动进行搜索,找出满足题目条件的路径。不过区别在于本题可以使用深度优先搜索也可以使用广度优先搜索,而《矩阵中的路径这道题》只能用深度优先搜索因为这道题要求按照单词必须按照字母顺序,通过相邻的单元格内的字母构成,相邻指的是水平或者垂直,这样,就限定了从一个点开始只能沿着左右上下的顺序,而不能按照层次的顺序,也就是按照某条给定路径走。(

              例如:[["a","b"],["c","d"]] ,不能得到路径abcd,因为a,b之后,b相邻的节点不是c。应该是abdc。这个给出了具体路径,而且路径只能按照某个点水平或者垂直去走。不能层次去遍历

)。本题是机器人每次可以上下左右移动,然后求机器人满足条件的所有经过的格子,这样可以深度优先从一个点开始找,或者直接按照层次一层一层去找都是可以的。

第一种方法:DFS(深度优先搜索)-非递归形式:

def movingCount(m: int, n: int, k: int) -> int:
    stack1, stack2 = [], []
    stack1.append((0, 0)) #用栈来保存元素,实现DFS
    while stack1:
        x, y = stack1[-1][0], stack1[-1][1] #拿到栈顶元素,先别出栈(其实直接出栈也是可以的,拿到栈顶元素进行处理罢了)
        numloc = (x % 10 + x // 10 + x // 100) + (
                    y % 10 + y // 10 + y // 100)  # 因为题目给的是n,m<=100,所以计算到百位即可。这个地方可以写一个函数代替计算操作。
        if numloc > k or x < 0 or x > m - 1 or y < 0 or y > n - 1: #当当前元素指针越界或者不符合条件,栈中元素出栈
            stack1.pop()
        elif numloc <= k and (x, y) not in stack2:
            stack2.append((x, y)) #将看到的元素加入栈2,当下一个元素不在栈2中且符合要求时再进行处理
            # 将当前节点的上下左右邻接点加入队列中(也就是当前节点下一步的所有路径),#优化:我们在搜索的过程中搜索方向可以缩减为向右和向下,而不必再向上和向左进行搜索(因为左上已经访问)。
            stack1.append((x, y + 1))
            stack1.append((x, y - 1))
            stack1.append((x + 1, y))
            stack1.append((x + 1, y))
        else:
            stack1.pop() #当元素不满足条件时,从栈中退出。
    return len(stack2)

 

第二种方式:DFS(深度优先搜索)-递归形式:(推荐使用这种,代码量少,不过要确定好递归条件和终止条件)

def get_numlocsum(n):
    res = 0
    while n > 0:
        res += n % 10 #获取整数位的末尾数字,然后将每一次的都相加就可以求的整数每个数位的和
        n = n // 10 #整数位向前移动一位
    return res

def movingCount2(m: int, n: int, k: int) -> int:
    # 这个地方队列和set都可以,队列保证了路径的顺序(要是输出路径时可以用到),set保证了不重复(但顺序是乱的)。
      #这个序列中的元素不会重复,因为这个序列保存的是处理过的元素,必须是之前没处理的才能加进去,处理过的元素不能二次加进去
    seed = set()
    def dfs(i, j):
        #递归终止条件:指针越界;元素处理过了;不符合题目中的小于等于k的条件
        if i < 0 or j < 0 or i > m - 1 or j > n - 1 or (i, j) in seed or get_numlocsum(i) + get_numlocsum(j) > k:
            return 0
        seed.add((i,j))
        # 沿着四个地方递归。
        return 1+dfs(i,j+1)\
               +dfs(i,j-1)\
               +dfs(i+1,j)\
               +dfs(i-1,j)

    return dfs(0,0

 

第三种方法:BFS(广度优先搜索):(这个可没有递归形式哈,按照他的性质来的)

# 一般来说,能采用DFS解决的问题,也能采用BFS解决,只是搜索策略不同(在题目没有给定要求的情况下,例如矩阵的路径这道题就只能使用DFS。)
def movingCount3(m: int, n: int, k: int) -> int:
    queue, seed = [], []
    queue.append((0, 0))
    while queue:
        s = queue.pop(0)
        x, y = s[0], s[1] #这个跟上边的DFS比,直接将元素出栈,减少了一些步骤,减少了一些时间。但采用不出栈,当不满足条件出栈时,这种情况便于理解。
        numloc = get_numlocsum(x) + get_numlocsum(y)  # 因为题目给的是n,m<=100,所以计算到百位即可。这个地方可以写一个函数代替计算操作。
        if numloc > k or x < 0 or x > m - 1 or y < 0 or y > n - 1:
            continue
        elif numloc <= k and (x, y) not in seed:
            # 将当前节点的上下左右邻接点加入队列中(也就是当前节点下一步的所有路径),#优化:我们在搜索的过程中搜索方向可以缩减为向右和向下,而不必再向上和向左进行搜索(因为左上已经访问)。
            seed.append((x, y))
            queue.append((x, y + 1))
            queue.append((x, y - 1))
            queue.append((x + 1, y))
            queue.append((x + 1, y))
    return len(seed)

执行结果:

输入7,2,3

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值