版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}
总结
虽然回溯法要注意探索和恢复现场两点,前提是结合题意来说的,如果探索的节点对于顺序性无要求,那么就不必要恢复现场了。