阅读 653

【剑指Offer-Swift】13.机器人的运动范围(DFS及优化、图解BFS)

题目

地上有一个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
复制代码

来源:力扣(LeetCode)

链接:leetcode-cn.com/problems/ji…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

一、 分析

矩阵搜索问题一般有两种思路:

  • 通过递归实现的深度优先DFS
    • 朝一个方向走到底,再回退
  • 用过队列实现的广度优先BFS
    • 按照“平推”的方式向前搜索

二、 解法一: DFS深度优先及优化

13-01.png

func movingCount(_ m: Int, _ n: Int, _ k: Int) -> Int {
    if m <= 0 || n <= 0 || k < 0 {
        return 0
    }
    var flags: [[Bool]] = Array(repeating: Array(repeating: false, count: n), count: m)
    return moveTo(0, 0, k, flags: &flags)
}
复制代码

2.1 核心递归逻辑

func moveTo(_ x: Int, _ y: Int, _ k: Int, flags: inout [[Bool]]) -> Int {
    if (x < 0 || x >= flags.first?.count ?? 0) ||
        (y < 0 || y >= flags.count) ||
        (flags[y][x] == true) ||
        (digitSum(x) + digitSum(y) > k)
    {
        return 0
    }
    
    flags[y][x] = true
    
    let count = 1
        + moveTo(x + 1, y, k, flags: &flags)
        + moveTo(x - 1, y, k, flags: &flags)
        + moveTo(x, y + 1, k, flags: &flags)
        + moveTo(x, y - 1, k, flags: &flags)
    
    return count
}
复制代码

2.2 数位和函数

func digitSum(_ num: Int) -> Int {
    var temp = num
    var result = 0
    while temp > 0 {
        result += temp % 10
        temp = Int(floor(Double(temp) / 10.0))
    }
    return result
}
复制代码

2.3 总结

  • 时间复杂度
    • 最差O(m*n)
  • 空间复杂度
    • 最差O(m*n)

三、 DFS优化

在深度优先的解法中,我们才拆分为了上下左右四个方向的子问题。

但实际上本题只需要右下两个方向就可以,因为上左一定是走过的。

优化后的核心 moveTo 代码:

13-02.png

func moveTo(_ x: Int, _ y: Int, _ k: Int, flags: inout [[Bool]]) -> Int {
    if (x < 0 || x >= flags.first?.count ?? 0) ||
        (y < 0 || y >= flags.count) ||
        (flags[y][x] == true) ||
        (digitSum(x) + digitSum(y) > k)
    {
        return 0
    }
    
    flags[y][x] = true
    
    let count = 1
        + moveTo(x + 1, y, k, flags: &flags)
        + moveTo(x, y + 1, k, flags: &flags)
    
    return count
}
复制代码

可以发现减少了多余的计算效率提升了不少,这点如果在面试中注意到的话是个很好的加分项哦

四、 解法二:广度优先

广度优先解法理解相比深度优先不够直观

func movingCount_bfs(_ m: Int, _ n: Int, _ k: Int) -> Int {
    var visited = Array(repeating: Array(repeating: false, count: n), count: m)
    var result = 0
    var queue:Array = [(0,0,0,0)]

    while !queue.isEmpty {
        let (y,x,si,sj) = queue.removeFirst()
        if y >= m || x >= n || k < si + sj || visited[y][x]{
            continue
        }
        visited[y][x] = true
        result = result + 1
        queue.append((y + 1, x, digitSum(y + 1), sj))
        queue.append((y, x + 1, si, digitSum(x + 1)))
    }
    return result
}
复制代码

13.03.png

4.1 图解

来源Krahets大佬

13-11.png

13-12.png

13-13.png

13-14.png

13-15.png

13-16.png

13-17.png

13-18.png

13-19.png

👋

My apps

-扫雷Elic 无尽天梯梦见账本
类型游戏财务
AppStoreElicUmemi
文章分类
iOS
文章标签