机器人的运动范围

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

解题思路:本题和link类似,属于典型搜索回溯问题,所以可以用深度优先搜索和广度优先搜索结局
首先题目给出了一个每个坐标数位和概念,就是一个数每位数字求和

int sums(int x)
    int s = 0;
    while(x != 0) {
        s += x % 10;
        x = x / 10;
    }
    return s;

根据题意,因为是计数,所以机器人是不能走已经走过的路的,因为是从左上角进行遍历的,所以只能走下和右两个路径

方式一:深度优先遍历DFS

  • 深度优先遍历:可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
  • 剪枝:在搜索过程中,如遇到数位和超出目标,或者元素已经被访问,则直接返回

算法解析:

  • 递归参数: 当前元素在矩阵中的行列索引 i 和 j ,矩阵行和列m,n,目标。
  • 终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 00 ,代表不计入可达解。

递推工作:

  • 标记当前单元格 :将索引 (i, j) 存入visited 中,代表此单元格已被访问过。
  • 搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
    回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数

代码:

class Solution {
    int m, n, k;
    boolean[][] visited;  //辅助矩阵,标记是否已经被访问过了

    public int movingCount(int m, int n, int k) {
        this.m = m;
        this.n = n;
        this.k = k;
        this.visited = new boolean[m][n];
        return dfs(0, 0, m, n, k);
    }

   /**
     * @param i  元素在矩阵中的索引行
     * @param j  元素在矩阵中的索引列
     * @param m  矩阵行数
     * @param n 矩阵列数
     * @param k  目标数位和
     * @return  递归中止条件:1.索引行或列越界
     * 2.位数和超出目标值k
     * 3.当前元素已经被访问过,返回0,代表不计入可达解
     */
    public int dfs(int i, int j, int m, int n, int k) {
        if (i >= m || j >= n || k < sums(i) + sums(j) || visited[i][j]) return 0;
        visited[i][j] = true;
        return 1 + dfs(i + 1, j, m, n, k) + dfs(i, j + 1, m, n, k);
    }

    //求数位和函数
    int sums(int x) {
        int s = 0;
        while (x != 0) {
            s += x % 10; //得到x个位数字
            x = x / 10;  //另x的十位数字向右移一位,即删除个位数字
        }
        return s;
    }
}

方式二:广度优先遍历BFS

  • BFSDFS两者目标都是遍历矩阵,但是方法不同,DFS是按照一个方向走到底,再回退,以此类推;BFS是按照"平推的方式"进行搜索
  • BFS通常利用队列来实现广度优先搜索

算法解析

  • 初始化:将机器人初始点 (0, 0)(0,0) 加入队列 queue
  • 递归终止条件queue 为空。代表已遍历完所有可达解。
  • 迭代流程
    1. 单元格出队:将队首单元格的 索引、数位和 弹出,作为当前搜索单元格
    2. 判断是否跳过:若 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,执行 continue
    3. 标记当前单元格:将单元格索引 (i, j) 存入visited 中,代表此单元格 已被访问过 。
    4. 单元格入队:将当前单元格下方右方加入queue
  • 返回值:使用辅助变量res自增记录解的数量
    在这里插入图片描述代码
    public int movingCount(int m, int n, int k) {
        boolean visited[][] = new boolean[m][n];
        int res = 0;
        //队列保存的是访问到的格子的坐标,是一个二维数组
        Queue<int[]> queue = new LinkedList<int[]>();
        queue.add(new int[]{0, 0});  //将第一个元素入队

        //队列为空时,代表已经完成所有可达解
        while (queue.size() > 0) {
            int[] x = queue.poll();  //将队首的元素即第一个单元格弹出,获得她的索引,作为当前搜索单元格
            int i = x[0], j = x[1];
            if (i >= m || j >= n || k < sums(i) + sums(j) || visited[i][j]) { //不满足条件,就跳过这个格子
                continue;
            }
            visited[i][j] = true; //标注已经被访问过
            res++; //结果集+1
            queue.add(new int[]{i + 1, j});  //将下方和右方元素继续入队
            queue.add(new int[]{i, j + 1});
        }
        return res;
    }
    //求数位和函数
    int sums(int x) {
        int s = 0;
        while (x != 0) {
            s += x % 10; //得到x个位数字
            x = x / 10;  //另x的十位数字向右移一位,即删除个位数字
        }
        return s;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值