题目链接
题目描述
在一个 的网格迷宫中,
0
代表通路,1
代表墙壁。起点固定为左上角的 ,终点固定为右下角的
。你需要找到一条从起点到终点的可行路径,并按顺序输出路径上每个格子的坐标。题目保证路径存在且唯一。
解题思路
本题是一个典型的迷宫寻路问题。我们需要在给定的网格中,找到一条从起点 到终点
的可行路径。由于题目保证路径存在且唯一,我们可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来解决。这里我们采用深度优先搜索(DFS)的方法。
DFS 的核心思想是“一条路走到黑”,即从起点开始,沿着一个方向不断探索,直到到达终点或遇到死路(墙壁或已访问过的格子)。如果遇到死路,就回溯到上一个路口,尝试其他方向。
为了能够输出最终的路径,我们不能只判断是否能到达终点,还需要记录下路径的轨迹。一个经典的方法是使用一个 数组(或哈希表),在搜索过程中,
记录我们是从哪个格子到达
的。
算法步骤如下:
-
初始化:创建一个二维的
数组来标记访问过的格子,避免重复搜索和陷入死循环。创建一个二维的
数组来存储每个格子的前驱节点。
-
递归搜索:从起点
开始进行 DFS。定义递归函数
:
a. 将当前格子
标记为已访问。
b. 如果
是终点,说明已找到路径,返回
。
c. 遍历当前格子的四个邻居
。如果邻居是有效的(在边界内、不是墙、且未被访问过):
i. 记录
。
ii. 递归调用
。如果返回
,说明通过这个邻居找到了通往终点的路径,立刻将
向上层返回,停止搜索其他分支。
d. 如果所有邻居都无法通向终点,返回
。
-
路径回溯:在主函数中调用
后,如果返回
,我们就可以从终点
开始,利用
数组一步步回溯到起点。
-
输出:将回溯得到的路径反转,即为从起点到终点的正确路径。按题目要求格式输出每个点的坐标。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
vector<vector<int>> grid;
vector<vector<bool>> visited;
vector<vector<pair<int, int>>> parent;
int dr[] = {-1, 1, 0, 0}; // 方向数组,上下左右
int dc[] = {0, 0, -1, 1};
// 深度优先搜索函数
bool dfs(int r, int c) {
visited[r][c] = true;
if (r == n - 1 && c == m - 1) {
return true; // 到达终点
}
for (int i = 0; i < 4; ++i) {
int nr = r + dr[i];
int nc = c + dc[i];
// 检查邻居是否合法
if (nr >= 0 && nr < n && nc >= 0 && nc < m && grid[nr][nc] == 0 && !visited[nr][nc]) {
parent[nr][nc] = {r, c}; // 记录父节点
if (dfs(nr, nc)) {
return true; // 找到了路径
}
}
}
return false; // 未找到路径
}
int main() {
cin >> n >> m;
grid.assign(n, vector<int>(m));
visited.assign(n, vector<bool>(m, false));
parent.assign(n, vector<pair<int, int>>(m, {-1, -1}));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> grid[i][j];
}
}
if (dfs(0, 0)) {
vector<pair<int, int>> path;
int r = n - 1, c = m - 1;
// 从终点开始回溯
while (r != -1 && c != -1) {
path.push_back({r, c});
pair<int, int> p = parent[r][c];
r = p.first;
c = p.second;
}
reverse(path.begin(), path.end()); // 反转路径
for (const auto& p : path) {
cout << "(" << p.first << "," << p.second << ")" << endl;
}
}
return 0;
}
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
static int n, m;
static int[][] grid;
static boolean[][] visited;
static Point[][] parent;
static int[] dr = {-1, 1, 0, 0}; // 方向数组
static int[] dc = {0, 0, -1, 1};
// 用于存储坐标点
static class Point {
int r, c;
Point(int r, int c) {
this.r = r;
this.c = c;
}
}
// 深度优先搜索函数
static boolean dfs(int r, int c) {
visited[r][c] = true;
if (r == n - 1 && c == m - 1) {
return true; // 到达终点
}
for (int i = 0; i < 4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
// 检查邻居是否合法
if (nr >= 0 && nr < n && nc >= 0 && nc < m && grid[nr][nc] == 0 && !visited[nr][nc]) {
parent[nr][nc] = new Point(r, c); // 记录父节点
if (dfs(nr, nc)) {
return true; // 找到了路径
}
}
}
return false; // 未找到路径
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
grid = new int[n][m];
visited = new boolean[n][m];
parent = new Point[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
grid[i][j] = sc.nextInt();
}
}
if (dfs(0, 0)) {
List<Point> path = new ArrayList<>();
int r = n - 1, c = m - 1;
// 从终点回溯
while (r != -1) { // 初始parent为null,其r,c默认为0,所以不能用r,c判断
path.add(new Point(r, c));
if (r == 0 && c == 0) break;
Point p = parent[r][c];
r = p.r;
c = p.c;
}
Collections.reverse(path); // 反转路径
for (Point p : path) {
System.out.println("(" + p.r + "," + p.c + ")");
}
}
}
}
import sys
# 增加递归深度限制以防迷宫过深
sys.setrecursionlimit(10000)
def main():
n, m = map(int, sys.stdin.readline().split())
grid = [list(map(int, sys.stdin.readline().split())) for _ in range(n)]
visited = [[False] * m for _ in range(n)]
parent = {} # 使用字典记录父节点
dr = [-1, 1, 0, 0] # 方向数组
dc = [0, 0, -1, 1]
# 深度优先搜索函数
def dfs(r, c):
visited[r][c] = True
if r == n - 1 and c == m - 1:
return True # 到达终点
for i in range(4):
nr, nc = r + dr[i], c + dc[i]
# 检查邻居是否合法
if 0 <= nr < n and 0 <= nc < m and grid[nr][nc] == 0 and not visited[nr][nc]:
parent[(nr, nc)] = (r, c) # 记录父节点
if dfs(nr, nc):
return True # 找到了路径
return False # 未找到路径
if dfs(0, 0):
path = []
curr = (n - 1, m - 1)
# 从终点回溯
while curr in parent:
path.append(curr)
curr = parent[curr]
path.append((0, 0)) # 添加起点
path.reverse() # 反转路径
for r, c in path:
print(f"({r},{c})")
if __name__ == "__main__":
main()
算法及复杂度
- 算法:深度优先搜索 (DFS)
- 时间复杂度:我们需要访问迷宫中的每个格子最多一次。对于每个格子,我们检查其四个方向的邻居。因此,总时间复杂度为
,其中
和
分别是迷宫的行数和列数。
- 空间复杂度:
,
,
数组都需要
的空间。
- DFS 的递归调用栈在最坏情况下(例如一条覆盖所有格子的蜿蜒路径)深度可能达到
。
- 因此,总空间复杂度为
。