迷宫问题_100分_A/B卷复用_递归/回溯

迷宫问题

题目描述:

定义一个二维数组 N*M,如 5 x 5 数组下所示:
  int maze[5][5] = {
    0 1 0 0 0
    0 1 1 1 0
    0 0 0 0 0
    0 1 1 1 0
    0 0 0 1 0
  }
它表示一迷宫,其中的 1 表示墙壁,0 表示可以走的路,
只能横着走或竖着走,不能斜着走,
要求编程序找出从左上角到右下角的最短路径
入口点为[00]既第一格是可以走的路。
数据范围: 2<=nm<=10,输入的内容只包含 0<=val<=1.

输入输出描述:

输入描述:

  输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的 1 表示墙壁,
  0 表示可以走的路。数据保证有唯一解不考虑有多解的情况,即迷宫只有一条通道。

输出描述:

  左上角到右下角的最短路径,格式如样例所示

示例1:

输入:
	5 5
	0 1 0 0 0
	0 1 0 1 0
	0 0 0 0 0
	0 1 1 1 0
	0 0 0 1 0
输出:
	(0,0)
	(1,0)
	(2,0)
	(2,1)
	(2,2)
	(2,3)
	(2,4)
	(3,4)
	(4,4)

示例2:

输入:
	5 5
	0 1 0 0 0
	0 1 0 1 0
	0 0 0 1 0
	0 1 1 1 0
	0 0 0 0 0
输出:
	(0,0)
	(1,0)
	(2,0)
	(3,0)
	(4,0)
	(4,1)
	(4,2)
	(4,3)
	(4,4)

解题思路:

迷宫问题_100分_A/B卷复用_递归/回溯_第1张图片
本题是一道经典的、可以直接套用常用的回溯模板的题目,直接使用回溯的方法就行了;
回溯的关键:从某一点出发,在每个点有几个选择(如上下左右、加减乘除),遍历这些选择,递归这些选择;总有一个递归终点,能够达到递归终点,说明这一路径上的选择是可行的(有的是要求从可行的方案中再挑出一个最优的解法,比如最短路径,最大最小值等)。

代码:

// 记录迷宫,1 表示墙壁,0 表示可以走的路
static int[][] matrix;
// 标记在递归回溯过程中,那些位置已经被访问过
static boolean[][] visited;
// 标记从左上角真的能够达到右下角(题目中说保证一定能够达到,所以该标志可有可无)
static boolean isBlock = true;
// 一个点可以到达它的上下左右,这个数组用来表示上下左右的方向
static int[][] direction = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
// 记录从左上角到达右下角的最短路径上的所有点
static List res;

public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	String[] split = scanner.nextLine().split(" ");
	int n = Integer.parseInt(split[0]);
	int m = Integer.parseInt(split[0]);
	matrix = new int[n][m];
	visited = new boolean[n][m];
	res = null;

	// 初始化矩阵
	for (int i = 0; i < n; i++) {
		split = scanner.nextLine().split(" ");
		for (int j = 0; j < m; j++) {
			matrix[i][j] = Integer.parseInt(split[j]);
		}
	}

	if (matrix[n - 1][m - 1] == 1) {
		System.out.println(-1);
		return;
	}

	List path = new ArrayList<>();
	path.add(new int[] {0, 0});
	// 一开始出发,从左上角 [0, 0] 位置开始,尝试到达右下角
	dfs(0, 0, path);

	// 如果能够成功达到右下角,则输出最短路径上的点
	if (!isBlock) {
		for (int[] item : res) {
			System.out.println("(" + item[0] + "," + item[1] + ")");
		}
	}
}

// 尝试从 [i, j] 这个位置到达终点,能达到的话是否是最短路径
private static void dfs(int i, int j, List path) {
	// 达到右下角,判断是否是最短路径,是则更新
	if (i == matrix.length - 1 && j == matrix[0].length - 1) {
		isBlock = false;
		// 此次能够顺利到达右下角,并且路径上的点比较少——故更新答案
		if (res == null || res.size() > path.size()) {
			res = new ArrayList<>(path);
		}
		return;
	}

	// 越界、被访问过、是墙(无法通过)
	if (i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length || visited[i][j] || matrix[i][j] == 1) {
		return;
	}

	// 标记 [i, j] 位置已经被访问过了。如果不进行标记,那么就会再访问,那就是走了回头路,进入死循环了
	visited[i][j] = true;
	// 上下左右
	for (int[] item : direction) {
		int newX = item[0] + i;
		int newY = item[1] + j;

		path.add(new int[] {newX, newY});
		dfs(newX, newY, path);
		// 回溯
		path.remove(path.size() - 1);
	}
	// 回溯
	visited[i][j] = false;
}

回溯类型题目

机器人走迷宫
仿 LISP 运算
全排列
最大时间
无向图染色
最大岛屿体积
羊、狼、农夫过河
考古学家
叠积木
用户调度问题
报文解压缩
最大平分数组
星际篮球争霸赛
等和子数组最小和

你可能感兴趣的:(华为OD机试真题(Java,A卷+B卷),华为od,华为,深度优先,java,算法)