0
点赞
收藏
分享

微信扫一扫

剑指offer面试题13:机器人的运动范围

最后的执着 2022-04-29 阅读 44
leetcode

题目描述:地上有一个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

1、深度优先DFS

深度优先搜索: 可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。

剪枝: 在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为可行性剪枝 

由题可知,机器人只能往右或者往下移动,当机器人移动时,需要查看要移动到的格子下标数位和是否大过 k,大了话就不给移动(提前进行剪枝返回),对于移动到过的格子,要做 true 处理。

代码讲解
        1、先新建一个 m 行 n 列的数组,全部初始化为 false
        2、进行深度优先遍历,如果索引越界、i 和 j 的数位和大于 k、该格子已遍历过,那么提前剪               枝,返回0
        3、如果符合条件,将数组该格子变为 true,代表已遍历
        4、继续进行递归,往右的和往下的,同时将递归的返回值 连同 + 1 进行返回

class Solution {
public:
    int movingCount(int m, int n, int k) 
    {
        int count;
        //临时变量visited记录格子是否被访问过
        vector<vector<bool>> visited(m,vector<bool>(n,false));
        return dfs(m,n,k,0,0,visited);
    }

    int dfs(int& m, int& n, int& k, int row, int col, vector<vector<bool>>& visited)
    {
        int row_ge = row%10;
        int row_shi = row/10;
        int col_ge = col%10;
        int col_shi = col/10;
        int sum = row_ge+row_shi+col_ge+col_shi;
        
        //越界 或 行列坐标的数位之和大于k 或 该格子已经访问过
        if(row<0 || row>=m || col<0 || col>=n || sum>k || visited[row][col]) return 0;
        //标注这个格子被访问过
        visited[row][col] = true;

        return 1+dfs(m,n,k,row+1,col,visited)+dfs(m,n,k,row-1,col,visited)
                +dfs(m,n,k,row,col-1,visited)+dfs(m,n,k,row,col+1,visited);  
    }
};

每个可以到达的格子,始终可以通过左边或者上方到达。 

回溯返回值: 返回   1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。

return 1+dfs(m,n,k,row+1,col,visited)+dfs(m,n,k,row,col+1,visited);

2、广度优先遍历 BFS
BFS/DFS : 两者目标都是遍历整个矩阵,不同点在于搜索顺序不同。DFS 是朝一个方向走到底,再回退,以此类推;BFS 则是按照“平推”的方式向前搜索。
BFS 实现: 通常利用队列实现广度优先遍历。
算法解析:
       1、初始化: 将机器人初始点 (0, 0) 加入队列 queue ;
       2、迭代终止条件: queue 为空。代表已遍历完所有可达解。
       3、迭代工作:
             (1) 单元格出队: 将队首单元格的坐标索引弹出,作为当前搜索单元格。
              (2)判断是否跳过: 若 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访                         问过时,执行 continue 。
              (3)标记当前单元格 :将单元格索引 (i, j) 存入 visited 中,代表此单元格 已被访问                         过 。
             (4)单元格入队: 将当前元素的下方、右方单元格的坐标索引加入 queue 。
    4、返回值: Set visited 的长度 len(visited) ,即可达解的数量。

class Solution {
public:
    int movingCount(int m, int n, int k) 
    {
        //临时变量visited记录格子是否被访问过
        vector<vector<bool>> visited(m,vector<bool>(n,false));

        //创建一个队列,保存的是访问到的格子坐标,是一组键值对
        queue<pair<int,int>> queue;
        //从左上角坐标[0,0]点开始访问,把坐标点加入到队列的队尾
        queue.push(make_pair(0,0));
        visited[0][0] = true;  //[0,0]坐标的格子已经访问
        int res = 1;  //能够到达的格子数

        // 向下和向右的方向数组
        int dx[2] = {0, 1};
        int dy[2] = {1, 0};

        while (queue.size() > 0) //while(!queue.empty())
        {
            //获取当前格子的坐标值
            pair<int,int> index = queue.front(); //auto index = queue.front();
            //这里的pop()函数表示的是移除队列头部元素,因为队列是先进先出,从尾部添加,从头部移除
            queue.pop();
        
            for(int i=0;i<2;i++)
           {
                //i=0时,坐标值+(0,1)表示向下移动一个格子
                //i=1时,坐标值+(1,0)表示向右移动一个格子
                int tx = index.first + dx[i];
                int ty = index.second + dy[i];

                //边界条件的判断,判断当前格子坐标是否满足条件,visited[tx][ty]判断这个格子是否被访问过
                if (tx < 0 || tx >= m || ty < 0 || ty >= n || k < Sum(tx, ty) || visited[tx][ty])
                {
                    continue;  //退出本次循环,进入下一次循环
                }
                //标注这个格子被访问过,并且插入到队列中
                visited[tx][ty] = true;
                queue.push(make_pair(tx,ty));
                res++;        
            }
        }
        return res;
    }

    int Sum(int x,int y) {
        int sum = 0;
        while(x > 0) 
        {
            sum += x % 10;
            x /= 10; 
        }
        while(y > 0)
        {
            sum += y % 10;
            y /=10;
        }
        return sum;
    }
};

 

 

举报

相关推荐

0 条评论