描述
地上有一个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
思路
此题和12题类似,是一个矩阵搜索问题,很容易想到利用 BFS 或 DFS 策略进行搜索。本题的搜索条件是数位之和小于 k且是联通的,且需要记录所有满足条件的格子数。
可以使用递归实现DFS搜索,由于本题矩阵中满足数位和的解构成的几何形状形是多等腰直角三角形,每个三角形的直角顶点位于
0, 10, 20, …0,10,20,… 等数位和突变的矩阵索引处 。三角形内的解虽然都满足数位和要求,但由于机器人每步只能走一个单元格,
而三角形间不一定是连通的,因此机器人不一定能到达,称之为 不可达解 ;同理,可到达的解称为可达解 (本题求此解) 。
根据可达解的结构和连通性,易推出机器人可 仅通过向右和向下移动,访问所有可达解。因此,本地DFS可以之搜索向下、向右两个方向。
求数位之和有一个通用的方法:利用%10取整数的最低位,再利用/10将十进制数右移,不断累加最低位的值直到数为0为止。但本题有一个更优秀的策略,由于机器人每次只能移动一格,因此数位每次递增1,此外,行坐标或列坐标发生进位时,数位之和总是要减去8,利用这个特点便可以计算机器人移动到的格子的数位之和。
如果使用DFS策略,利用递归回溯很容易计算出所有满足条件的格子总数,但如果使用BFS,那么只能设置一个全局变量来记录各自总数。
代码
// DFS
class Solution {
int m,n,k;
boolean[][] board = null;
public int movingCount(int m, int n, int k) {
this.m = m;
this.n = n;
this.k = k;
board = new boolean[m][n];
return dfs(0, 0, 0, 0);
}
private int dfs(int i, int j, int si, int sj) {
// 如果发生越界、当前格子已访问过、不满足搜索条件(数位之和大于k),则当前格子不作数,返回0
if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || board[i][j] || si + sj > k) return 0;
// 否则,将当前格子标记为已访问过,并继续往右、往下搜索
board[i][j] = true;
return 1 + dfs(i + 1, j, (i + 1) % 10 == 0 ? si - 8 : si + 1, sj) + dfs(i, j + 1, si, (j + 1) % 10 == 0 ? sj - 8 : sj + 1);
}
}
// BFS
class Solution {
public int movingCount(int m, int n, int k) {
int sum = 0;
boolean[][] board = new boolean[m][n];
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{0,0,0,0});
while (!queue.isEmpty()) {
int[] pos = queue.poll();
if (pos[0] < 0 || pos[0] > m - 1 || pos[1] < 0 || pos[1] > n -1 || pos[2] + pos[3] > k || board[pos[0]][pos[1]]) continue;
board[pos[0]][pos[1]] = true;
sum++;
int si = (pos[0] + 1) % 10 == 0 ? pos[2] - 8 : pos[2] + 1;
int sj = (pos[1] + 1) % 10 == 0 ? pos[3] - 8 : pos[3] + 1;
queue.add(new int[]{pos[0] + 1, pos[1], si, pos[3]});
queue.add(new int[]{pos[0], pos[1] + 1, pos[2], sj});
}
return sum;
}
}