Day18 机器人的运动范围

题目:
地上有一个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原题链接

思路:
DFS+剪枝,类似《矩阵中的路径》
思路参考

定义两个子函数

  • nowsum(m,n),计算当前坐标(m,n)的数位之和
  • dfs(m,n,visit,k,i,j),对当前坐标(m,n)的上下左右进行遍历判断

代码:

class Solution:
    def movingCount(self, m: int, n: int, k: int) -> int:
        def nowsum(m,n):#当前坐标(m,n)的数位之和
            p,q=len(str(m)),len(str(n))
            s=0
            for i in range(p-1,-1,-1):
                s+=m//(10**i)
                m=m%(10**i)
            #此时的s是m的数位和
            for i in range(q-1,-1,-1):
                s+=n//(10**i)
                n=n%(10**i)
            return s#此时s是m和n的数位之和
        
        def dfs(m,n,visit,k,i,j):
            if not 0 <= i < m or not 0 <= j < n: return 0#索引越界
            elif nowsum(i,j)>k: return 0#数位和大于k,不符合要求
            elif (i,j) in visit: return 0#该格已被访问过,不符合要求
            visit.append((i,j))
            return 1+dfs(m,n,visit,k,i + 1, j) + dfs(m,n,visit,k,i - 1, j) + dfs(m,n,visit,k,i, j + 1) + dfs(m,n,visit,k,i, j - 1)

        visit=list()
        return dfs(m,n,visit,k,0,0)


优化
由于机器人的起始位置是坐标为[0, 0]的点,即左上角,所以仅考虑向下和向右的移动方向即可 ,优化后执行时间减半,内存消耗明显减少

return 1+dfs(m,n,visit,k,i + 1, j) + dfs(m,n,visit,k,i, j + 1)

错误代码:

  • 思路:两个for循环遍历方格中所有区域,进行当前位置数位和与k的判断,若符合要求,count+1
  • 错误原因:丢弃了一个重要特征,题目背景是机器人的移动,每次可以向左、右、上、下移动一格,不能横跨多个格子移动。
    例如
    • 输入m=16,n=8,k=4,符合题目要求的格子有15个
      i=0,j=0,1,2,3,4
      i=1,j=0,1,2,3
      i=2,j=0,1,2
      i=3,j=0,1
      i=4,j=0
    • 而代码运行结果还会有i=10,j=0,1,2,3,4…等
  • 错误在于,当出现第一个不符合要求的格子时【i=0,j=5】执行continue【结束本次循环,直接进行下一次循环的判断条件】,只结束了本次循环,仍会对下一个元素【i=0,j=5】进行判断,继续遍历直至【i=10,j=0】,判断发现数位和小于k,count执行+1,实际上,机器人无法移动至该方格
class Solution:
    def movingCount(self, m: int, n: int, k: int) -> int:
        def nowsum(m,n):#当前坐标(m,n)的数位之和
            p,q=len(str(m)),len(str(n))
            p_sum=0
            for i in range(p-1,-1,-1):
                p_sum+=m//(10**i)
                m=m%(10**i)
            #此时的p_sum是m的数位和
            for i in range(q-1,-1,-1):
                p_sum+=n//(10**i)
                n=n%(10**i)
            return p_sum#此时p_sum是m和n的数位之和
        count=0
        for i in range(m):
            for j in range(n):
                if nowsum(i,j)<=k:
                    count+=1
                else:
                    continue
        return count
  • 企图用break语句【强制退出当前for】来修改。
  • 修改后效果:成功通过示例1,示例2失败
    • 示例1:仍然输入m=16,n=8,k=4
      • 当i=0时,j=0,1,2,3,4符合要求
        直至j=5,数位和大于k(4),进入else语句,j重新赋值为0,break,强制终止内层循环,不再对j=6,7…进行遍历+判断。跳出内层for循环后,判断下一个i值,在j=0的条件下是否符合【数位和不大于k】的要求,若不符合,则再强制跳出外层for循环,直接return此时count值。
      • i=4时,j=0,符合要求,j=1,数位和大于k,进入else语句,j重新赋值0,break跳出内层for循环(i=4时的情况),对下一个i进行遍历,i=5时最小数位和都大于k,判定比5更大的i也不可能符合要求。直接break跳出外层循环,return。
    • 示例2:m=38,n=15,k=9
      • i=0,j=0,1,2…14;
      • 【错误】i=1,j=0,1,2,3,4,5,6,7,8符合要求,j=9时数位和大于k(9),判定i=1时,符合要求的格子已搜寻完毕,break跳出内层for循环(i=1的情况)
      • i=1,j=10,这个格子符合要求,可以从【i=0,j=10】此格往下移动一格
  • 放弃debug,采用递归来做【类似《矩阵中的路径》】
        count=0
        for i in range(m):
            for j in range(n):
                if nowsum(i,j)<=k:
                    count+=1
                else:
                    j=0
                    break
            if nowsum(i,j)>k:
                break
        return count

额外学到的知识:

  • continue和break用于改变for循环的执行流程。二者区别如下:

continue:用来结束当前当次循环,跳出循环体中下面尚未执行的语句,但不跳出当前循环。

for i in range(4):
	if i>1:
		print("执行了if语句")
		continue
	print("执行了后续语句")

i=0时,if判断,False,不进入if内的语句,执行print("执行了后续语句")
i=1,if判断,False,不进入if内的语句,执行print("执行了后续语句")
i=2,if判断,True,进入if内语句,执行print("执行了if语句"),执行continue,结束本次i=2的循环,不再执行后续语句,进行下一次for循环遍历(i=3)
i=3,if判断,True,进入if内语句,执行print("执行了if语句"),执行continue,结束本次i=2的循环,不再执行后续语句,后续无 需执行的元素。

break:跳出(最内层)循环,不再执行

for i in range(4):
	if i>1:
		print("执行了if语句")
		break
	print("执行了后续语句")

i=0时,if判断,False,不进入if内的语句,执行print("执行了后续语句")
i=1,if判断,False,不进入if内的语句,执行print("执行了后续语句")
i=2,if判断,True,进入if内语句,执行print("执行了if语句"),执行break,结束for循环。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值