[剑指-Offer] 13. 机器人的运动范围(回溯、DFS、递归、代码优化)

1. 题目来源

链接:机器人的运动范围
来源:LeetCode——《剑指-Offer》专项

2. 题目说明

地上有一个 mn 列的方格,从坐标 [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

3. 题目解析

方法一:DFS、回溯、bool矩阵

典型 DFS 应用,也就是回溯法,主要以下几点思路:

  • 以二维数组中每一个数都作为起点和给定字符串做匹配
  • 需要一个和原数组等大小的 boolvisited 数组用来记录当前位置是否已经被访问过
  • 若访问未越界、该点没被访问过、满足数位条件,即该点满足要求,将 bool 数组置位,count 加一记录即可,其它情况直接返回

参见代码如下:

// 执行用时 :464 ms, 在所有 C++ 提交中击败了7.01%的用户
// 内存消耗 :158.5 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int movingCount(int m, int n, int k) {
        int tmp = m * n;
        vector<bool> visited(tmp, false);
        int count = movingCountCore(m, n, k, 0, 0, visited);
        return count;
    }

    int movingCountCore(int m, int n, int k, int row, int col, vector<bool>& visited) {
        int count = 0;
        if (check(m, n, k, row, col, visited)) {
            visited[row * n + col] = true;
            count = 1 + movingCountCore(m, n, k, row - 1, col, visited) 
                      + movingCountCore(m, n, k, row, col - 1, visited) 
                      + movingCountCore(m, n, k, row + 1, col, visited)
                      + movingCountCore(m, n, k, row, col + 1, visited);
        }
        return count;
    }

    bool check(int m, int n, int k, int row, int col, vector<bool>& visited) {
        if (row >= 0 && row < m && col >= 0 && col < n 
            && getDigitSum(row) + getDigitSum(col) <= k 
            && !visited[row * n + col])
            return true;
        return false;
    }

    int getDigitSum(int number) {
        int sum = 0;
        while (number > 0) {
            sum += number % 10;
            number /= 10;
        }
        return sum;
    }
};

方法二:DFS、回溯、代码优化

思路和上面一致,在此上面代码过于冗余,在此对其进行修改。

参见代码如下:

// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :9.6 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int nums = 0;
    int movingCount(int m, int n, int k) {
        vector<vector <int> > arr(m, vector<int>(n, 0));
        if(k == 0)
            return 1;
        dfs(m, n, k, arr, 0, 0);
        return nums;
    }
    void dfs(int m, int n, int k, vector<vector <int> >& arr, int i, int j){
        if(i >= m || j >= n || i < 0 || j < 0)
            return;
        if(arr[i][j] == 1)
            return;
        if((i % 10 + i / 10 + j % 10 + j / 10) > k)
            return;
        arr[i][j] = 1;
        nums++;
        dfs(m, n, k, arr, i - 1, j);
        dfs(m, n, k, arr, i + 1, j);
        dfs(m, n, k, arr, i, j - 1);
        dfs(m, n, k, arr, i, j + 1);
    }
};

方法三:DFS性质、回溯、递归、代码优化

对于这道题来讲,是从 (0, 0) 出发。在广度优先算法 BFS 中,由于可行解的连通性和结构,仅考虑向下和向右的移动方向即可,同理深度优先算法 DFS

// 执行用时 :4 ms, 在所有 C++ 提交中击败了91.38%的用户
// 内存消耗 :9.3 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int nums = 0;
    int movingCount(int m, int n, int k) {
        vector<vector <int> > arr(m, vector<int>(n, 0));
        if(k == 0)
            return 1;
        dfs(m, n, k, arr, 0, 0);
        return nums;
    }
    void dfs(int m, int n, int k, vector<vector <int> >& arr, int i, int j){
        if(i >= m || j >= n || i < 0 || j < 0)
            return;
        if(arr[i][j] == 1)
            return;
        if((i % 10 + i / 10 + j % 10 + j / 10) > k)
            return;
        arr[i][j] = 1;
        nums++;
        dfs(m, n, k, arr, i + 1, j);
        dfs(m, n, k, arr, i, j + 1);
    }
};
发布了307 篇原创文章 · 获赞 125 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/104488204