Java 迷宫问题,复杂多条件迷宫问题

回溯法是一种不断试探且及时纠正错误的搜索方法,下面的求解过程采用回溯法。从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达一个新点,否则试探下一个方向;若所有的方向均没有通路,则沿原路返回前一点,换下一个方向继续试探,直到所有可能的通路都搜索到,或找到一条通路,或无路可走又返回到入口点。这里可以用一个栈来实现,每走一步,将该位置压入栈中,若该点无路可走,则出栈返回上一位置。

总结与思路:

一般的简单迷宫问题,类似于能否出去。可以使用“染色”的思路,一个节点被染红之后传给相邻的节点,最后如果出口节点也被染红,那么可以出去。从代码实现上看,

  1. 传入当前节点的信息,每次方法开始,判断是否为出口。是就可以提前退出,不是则继续向下执行。
  2. 建立迷宫大小的静态boolean数组,标识每个节点是否已被染色,避免死循环。
  3. 将本节点染色,判断是否有上下左右节点,有且未被染色,则调用本方法并传入将该节点信息(即递归调用)。
  4. 方法调用完毕,判断出口是否已经被染色。

复杂迷宫问题,查找最短路径等。

  1. 基本步骤同上,需注意几个问题。
  2. 考察最短路径,不同路径之间可以有一段重复。不应该再使用全局的Boolean数组。可以考虑使用List.contains()方法避免有重复的点。
  3. 需要保存路径时,使用回溯法。一般应使用stack,本题使用的是list,在执行下一次方法前将本节点加入list。然后一直向前探索,如果到了出口则保存下来,没到就不保存。递归方法返回时应该将本节点移除list,因为如果没到出口说明此路不通,无需保存本节点,如果到了出口,则已经加到result中了,不需要在保存本节点了。都应当移除,再从上一级节点探索。


遇到滴滴笔试题,迷宫上加了些其他因素

import java.util.*;

/**
 * @author XF 迷宫求路径,类似“染色”
 */
public class Main {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		// 输入参数 测试:4 4 10 1 0 0 1 1 1 0 1 0 1 1 1 0 0 1 1
		int n = sc.nextInt();
		int m = sc.nextInt();
		int p = sc.nextInt();
		int[][] mi = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				mi[i][j] = sc.nextInt();
			}
		}
		flag = new boolean[n][m];
		List result = new ArrayList<>();
		List tempList = new ArrayList<>();
		
		// 调用计算方法,传入参数。获得result.
		cc(0, 0, mi, result, tempList);

		// 比较消耗精力最少的
		int shortestNum = 0;
		List temp = (List) result.get(0);
		int min = (int) temp.get(temp.size() - 1);
		if (result.size() > 1) {
			for (int i = 1; i < result.size(); i++) {
				temp = (List) result.get(i);
				int tempMin = (int) temp.get(temp.size() - 1);
				if (tempMin < min) {
					min = tempMin;
					shortestNum = i;
				}
			}
		}
		List r = (List) result.get(shortestNum);
		r.remove(r.size() - 1);
		for(int i=0; i<r.size() ;i++){
			System.out.print(r.get(i)+",");
		}
		System.out.println("[0,3]");
		
	}

	// 辅助计算的静态变量
	static boolean[][] flag; // 为数组每个元素设置标志,避免重复到达某点造成多次计算或死循环
	static int pu; // pUsed 每个成功到达出口的序列计算消耗的体力值

	/*
	 * 计算迷宫路径方法,主要使用递归调用每个节点的上下左右。上下左右节点又可以调用它的上下左右。 按消耗体力的数量及出口位置,优先顺序为上、右、下、左
	 * 传入参数,形参,即引用变量,地址不变,但指向的地址的值可以变化.
	 */
	public static void cc(int i, int j, int[][] mi, List result, List tempList) {
		// flag[i][j] = true;
		// //本题是考察最短路径,不同路径之间可以有一段重复。应使用list.contains()判断是否已包含此节点。
		// 不管从哪条路到了出口,新建list存入结果,并保存pu
		if (i == 0 && j == mi[0].length - 1) {
			tempList.add(pu);
			result.add(new ArrayList<>(tempList));
			tempList.remove((Integer) pu);
		}
		/*
		 * up,从当前节点,考虑向上。如果上个节点有且没有使用,将上个节点作为参数传入方法。其他同理。
		 * 将当前节点信息(x,y)存入list,调用完方法移除!!!非常重要!!核心!!
		 * 因为不管是否到达出口,都说明已经探索完从这个节点伸出的路径了,应当移除,则可以从此节点上个节点继续探索。
		 * 到达了出口,已经保存了,该移除;没到,此路不通,移除。 根据需求应当使用stack,我使用不多就用list代替了。
		 */
		if (i > 0 && mi[i - 1][j] == 1
				&& !tempList.contains("[" + (i - 1) + "," + j + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 3;
			cc(i - 1, j, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 3;
		}
		// right
		if (j < mi[0].length - 1 && mi[i][j + 1] == 1
				&& !tempList.contains("[" + i + "," + (j + 1) + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 1;
			cc(i, j + 1, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 1;
		}
		// down
		if (i < mi.length - 1 && mi[i + 1][j] == 1
				&& !tempList.contains("[" + (i + 1) + "," + j + "]")) {
			tempList.add("[" + i + "," + j + "]");
			cc(i + 1, j, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
		}
		// left
		if (j > 0 && mi[i][j - 1] == 1
				&& !tempList.contains("[" + i + "," + (j - 1) + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 1;
			cc(i, j - 1, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 1;
		}
	}
}
下面几个网上的迷宫问题传送门:

 染色

stack 解决迷宫问题

A*算法 Java代码实现

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值