文章摘要:
- 题目要求在给定的二维网格中,从起点到终点的所有路径必须经过所有无障碍方格且不重复。采用深度优先搜索(DFS)算法,先统计无障碍方格总数,再从起点开始递归搜索四个方向。当到达终点时检查路径步数是否等于总步数,相等则计入有效路径。通过回溯和访问标记确保不重复访问。最终返回所有有效路径的数量。
文章目录
题目链接: 980. 不同路径Ⅲ
一、题目解析
题目给出了一个二维网格 grid ,每一个方格中的数字都有不同含义:
1表示起始方格,在一个二维网格中只有一个起始方格2表示结束方格,在一个二维网格中只有一个起始方格0表示可以走的方格-1表示有障碍的方格,无法走
在每一个方格上可以往上下左右四个方向 走下一步,我们需要返回从起始方格 到结束方格的所有路径。
但是,存在限制条件:每一个无障碍的方格都需要通过一次,且每一条路径中的一个方格只能走一次。
因此我们需要确保每一条路径都要有效(即通过每一个无障碍方格 且没有重复走方格)。
例如,题目给出的二维网格如下:
- grid = [ [ 1, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 2 ] ]
| 1 | 0 | 0 | 0 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 2 |
则有效的路径如下:
- (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
| 1➡️ | 0➡️ | 0➡️ | 0⬇️ |
|---|---|---|---|
| ⬇️0 | ⬅️0 | ⬅️0 | ⬅️0 |
| 0➡️ | 0➡️ | 0➡️ | 2🛑 |
- (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
| 1➡️ | 0⬇️ | 0➡️ | 0⬇️ |
|---|---|---|---|
| ⬇️0 | ⬅️0 | 0⬆️ | 0⬇️ |
| 0➡️ | 0➡️ | 0⬆️ | 2🛑 |
- (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
| 1⬇️ | 0➡️ | 0➡️ | 0⬇️ |
|---|---|---|---|
| 0⬇️ | ⬆️0 | ⬅️0 | 0⬇️ |
| 0➡️ | 0➡️ | 0⬆️ | 2🛑 |
- (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
| 1⬇️ | 0➡️ | 0➡️ | 0⬇️ |
|---|---|---|---|
| 0⬇️ | 0⬆️ | ⬇️0 | ⬅️0 |
| 0➡️ | 0⬆️ | ➡️0 | 2🛑 |
二、算法原理 + 代码实现
我们采用暴搜的思路解决这道题目,但是需要先遍历网格一遍,目的是统计一下网格中无障碍的方格的个数 ,然后加上起始位置和结束位置,结果是得到从起始方格到结束方格需要走过的总步数,这样能够筛选出通过所有无障碍方格的有效路径。
然后我们找到起始位置(即数字为 1 的方格),从起始位置开始深度优先遍历进行暴搜,递归搜索的时候维护一个用于记录步数的整型变量,每递归一个方格就加一。
当搜索到结束方格(数字为 2 )时,先看看记录步数的变量的值是否与之前统计的总步数相等,若不相等表示不是有效路径,若相等则表示是有效路径,这时候更新结果。
全局变量
为了方便递归,将题目给出的 grid 改为全局变量;整型变量 m,n 表示二维网格的 行数 和 列数;然后是记录从起始方格到结束方格的总步数 step 和 记录有效路径数 ret。
为了在递归搜索一条路径时不会重复走方格,我们依然利用一个和 grid 大小相同的布尔数组 visit 来实现;同时,为了在某个位置往上下左右四个方向搜索,我们定义两个向量数组 dx 和 dy,其具体原理在 【递归算法】单词搜索 一文中。
java
int[][] grid;
int m, n, step, ret;
boolean[][] visit;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
dfs 函数
函数头
我们给 dfs 函数设置的任务是:往指定位置的上下左右四个方向搜索并记录下递归的总步数。
因此,函数的参数需要知道 指定位置的下标 [row, col],同时设置一个整型变量参数 path 用于统计路径的步数。
java
dfs(int row, int col, int path);
函数体
进入 dfs 函数时,先判断一下当前方格是否为结束方格(数字为 2),如果是结束方格,说明一条路径已经结束,这时候判断该路径是否是有效路径(通过记录路径步数的函数参数 path 和全局总步数比较是否相等),若是有效的路径,就更新结果,否则直接返回上一层。
然后再循环四次分别搜索该位置的上下左右四个方向,当满足 下标合法 且 方格未被走过 且 方格数字不为 -1 时,才继续递归搜索。
细节问题
回溯
回溯操作就是将 visit 数组的值修改回 false 即可。
剪枝
本题采用暴搜的策略解决,因此不涉及到剪枝的操作。
递归出口
递归出口就是遇到结束方格时返回,在 dfs 函数体中实现。
代码实现
java
class Solution {
int[][] grid;
int m, n, step, ret;
boolean[][] visit;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
public int uniquePathsIII(int[][] givenGrid) {
grid = givenGrid; m = grid.length; n = grid[0].length;
visit = new boolean[m][n];
// 先遍历一遍二维网格,统计网格中无障碍的方格数并记录下起始位置的坐标
int x = 0, y = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
// 无障碍方格
step++;
}
if (grid[i][j] == 1) {
// 找到起始位置,记录坐标
x = i; y = j;
}
}
}
// 加上起始位置和结束位置的步数,就是所有步数
step += 2;
// 从起始位置开始深搜
visit[x][y] = true;
dfs(x, y, 1);
return ret;
}
private void dfs(int row, int col, int path) {
if (grid[row][col] == 2) {
// 遇到结束方格,判断该路径是否有效
if (path == step) {
// 是有效路径
ret++;
}
return;
}
// 从[row,col]的上下左右四个方向继续深搜
for (int k = 0; k < 4; k++) {
int x = row + dx[k], y = col + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n) {
if (!visit[x][y] && grid[x][y] != -1) {
visit[x][y] = true;
dfs(x, y, path + 1);
visit[x][y] = false;
}
}
}
}
}
文章到这里就告一段落啦,若有错误请尽管指出~
完