剑指Offer-68-机器人的运动范围

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dawn_after_dark/article/details/84196956

项目地址:https://github.com/SpecialYy/Sword-Means-Offer

问题

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

解析

意思是从方格的左上角到右下角所有可走的节点的总数,虽然题目说了可以走四个方向,其实走右,下两个方向即可包括全部过程。因为路径的整体方向是左上到右下,所以我们走右,下就可以到达方格的最后一格。

对于这种不确定走多少格子,且在每个节点都有多种选择问题都可以用回溯法来解决。

此题与这道题类似。

思路一

思路一就是通过经典的回溯法来解决,又称深度优先遍历。回溯法是思想其实就是暴力穷举法,它在所有的解空间寻找可行解,只不过它不是盲目的穷举,而是按照一定规则(也就是深度优先)来搜索可行解。这种策略广泛用在搜索过程中会面临多种选项的应用问题上。

我们从(0,0)出发,每次都从右和下两个方向中选择一个方向进行前进,直到走到方格边界或位于不合法节点(题目的约束条件)就向上一个节点回溯,然后换另一个方向继续探索。因为题目要求统计可以走的节点数,所以我们在遍历的过程,都会统计一个节点的两个方向中可走的节点数。这样就需要对统计过节点进行标注已访问过,避免重复计数。用一个与方格同样大小的标记数组即可解决访问状态问题。

	public int movingCount(int threshold, int rows, int cols) {
        if (threshold < 0 || rows <= 0 || cols <= 0) {
            return 0;
        }
        boolean[][] visited = new boolean[rows][cols];
        return dfs(threshold, rows, cols, 0, 0, visited);
    }

    public int dfs(int threshold, int rows, int cols, int row, int col,
                   boolean[][] visited) {
        if (row < 0 || row >= rows || col < 0 || col >= cols
                || visited[row][col] || !isValid(row, col, threshold)) {
            return 0;
        }
        int count = 1;
        //标记已访问过的节点
        visited[row][col] = true;
        //因为是从左上到右下,所以只需探索右,下方向即可
        count += dfs(threshold, rows, cols, row + 1, col, visited);
        count += dfs(threshold, rows, cols, row , col + 1, visited);
        //note: 此处不用恢复现场,因为这里的节点访问不要求顺序性,所以要避免重复访问。
        return count;
    }

    /**
     * 判断当前节点是否满足约束
     * @param row
     * @param col
     * @param threshold
     * @return
     */
    private boolean isValid(int row, int col, int threshold) {
        int result = countOfEachDigit(row);
        result += countOfEachDigit(col);
        return result <= threshold;
    }

    /**
     * 求一个数各位的之和
     * @param num
     * @return
     */
    private int countOfEachDigit(int num) {
        int result = 0;
        while (num != 0) {
            result += num % 10;
            num /= 10;
        }
        return result;
    }

总结

虽然回溯法要注意探索和恢复现场两点,前提是结合题意来说的,如果探索的节点对于顺序性无要求,那么就不必要恢复现场了。

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/84196956