剑指–机器人的运动范围
1,题目:
2,思路:
深度优先遍历 DFS
算法解析:
- 1.递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
- 2.终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 0 ,代表不计入可达解。
- 3.递推工作:
- 4.标记当前单元格 :将索引 (i, j) 存入 visited 中,代表此单元格已被访问过。
- 5.搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
- 6.回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
下面是对应的图解:
方法二:广度优先:
算法解析:
- 1.初始化: 将机器人初始点 (0, 0) 加入队列 queue ;
- 2.迭代终止条件: queue 为空。代表已遍历完所有可达解。
- 3.迭代工作:
- 4.单元格出队: 将队首单元格的 索引、数位和 弹出,作为当前搜索单元格。
- 5.判断是否跳过: 若 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,执行 continue 。
- 6.标记当前单元格 :将单元格索引 (i, j) 存入 visited 中,代表此单元格 已被访问过 。
- 7.单元格入队: 将当前元素的 下方、右方 单元格的 索引、数位和 加入 queue 。
- 8.返回值: visited 的长度 len(visited) ,即可达解的数量。
下面是对应的图解:
3,代码:
深度优先遍历 DFS
class Solution {
/*
算法解析:
1.递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
2.终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 0 ,代表不计入可达解。
3.递推工作:
4.标记当前单元格 :将索引 (i, j) 存入 visited 中,代表此单元格已被访问过。
5.搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
6.回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
*/
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, 0, 0);
}
public int dfs(int i, int j, int si, int sj) {
if(i >= m || j >= n || k < si + sj || visited[i][j]) //当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 0 ,代表不计入可达解。
return 0;
visited[i][j] = true;//标记当前单元格 :将索引 (i, j) 存入 visited 中,代表此单元格已被访问过。
return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj) + dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8);//回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
}
}
深度优先的另一种写法:
class Solution {
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
return dfs(visited, m, n, k, 0, 0);
}
private int dfs(boolean[][] visited, int m, int n, int k, int i, int j) {
if(i >= m || j >= n || visited[i][j] || bitSum(i) + bitSum(j) > k) return 0;
visited[i][j] = true;
return 1 + dfs(visited, m, n, k, i + 1, j) + dfs(visited, m, n, k, i, j + 1) ;
}
private int bitSum(int n) {
int sum = 0;
while(n > 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
}
方法二:广度优先:
class Solution {
public int movingCount(int m, int n, int k) {
/*
方法二:广度优先遍历 BFS
算法解析:
1.初始化: 将机器人初始点 (0, 0) 加入队列 queue ;
2.迭代终止条件: queue 为空。代表已遍历完所有可达解。
3.迭代工作:
4.单元格出队: 将队首单元格的 索引、数位和 弹出,作为当前搜索单元格。
5.判断是否跳过: 若 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,执行 continue 。
6.标记当前单元格 :将单元格索引 (i, j) 存入 visited 中,代表此单元格 已被访问过 。
7.单元格入队: 将当前元素的 下方、右方 单元格的 索引、数位和 加入 queue 。
8.返回值: visited 的长度 len(visited) ,即可达解的数量。
*/
boolean[][] visited = new boolean[m][n];
int res = 0;
Queue<int[]> queue= new LinkedList<int[]>();
queue.add(new int[] { 0, 0, 0, 0 });
while(queue.size() > 0) {
int[] x = queue.poll();
int i = x[0], j = x[1], si = x[2], sj = x[3];
if(i >= m || j >= n || k < si + sj || visited[i][j]) continue;
visited[i][j] = true;
res ++;
queue.add(new int[] { i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj });
queue.add(new int[] { i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 });
}
return res;
}
}