深度优先搜索,求解N(3)维地牢问题

**

Problem

**
你正身陷一个3维的地牢中,需要找到最快的路径离开。地牢由立方体单元组成,这些单元或者是岩石,或者不是岩石。你可以用一分钟的时间向东,向西,向南,向北,向上,或者向下,走到下一个单元中。你不能走对角线,迷宫的周围是坚硬的岩石。
可能逃离地牢吗?如果可能,要多长时间?

输入:

输入由许多地牢组成。每个地牢的描述的第一行是3个整数L,R和C(大小限制在30以内)。
L表示地牢的层数。
R和C表示地牢的行和列。
后面跟着L块,每块包含R行,每行包含C个字符,每个字符表示地牢的一个单元。一个单元如果由岩石构成,用‘#’表示;空的单元用‘.’表示;你所在的起始位置用‘S’表示;出口用‘E’表示。每层地牢后跟一空行。输入以为L,R和C赋0结束。
**

输出:

**
每个迷宫产生一行输出,如果可以到达出口,输出形式为
Escaped in x minute(s).
其中x是逃离的最短时间。
如果不可能逃离,输出
Trapped!
样例输入 样例输出
3 4 5
S…
.###.
.##…
###.#

##.##
##…

#.###
####E

1 3 3
S##
#E#

0 0 0 Escaped in 11 minute(s).
Trapped!

这个题目涉及到的知识点:

  • 深度优先搜索(递归)
  • ArrayList的应用

解题思路

数据结构+算法
1、看到题,首先需要输入3个数,需要三个变量来存储三位地宫的维度、宽、长,这里设置为k,m,n。
2、接下来构造k层、m行、n列的迷宫,选用ArrayList作为容器。这里我采用将k层拆开成k个1层。
3、深度优先搜索需要一个visited[]数组记录已经走过的点。在构造迷宫的时候,visited的大小需与迷宫相同,因为每个点都要存储该点是否被访问。
4、在构造迷宫的时候,还需要记录起点、终点。
5、在深度优先搜索中,每个点都看成是独立的点,下次访问的方式都有六种:向上、下、前、后、左、右。所以需要构造一个dir[][]数组,表示方向。

关于深度优先的思想,百度有很多解释,笔者就不再赘述。希望大家可以用代码跑一遍就明白了。

数据结构:

public static int k = 0;
public static int m = 0;
public static int n = 0;
public static int t = 0;
public static boolean visited[][];
public static ArrayList<Integer> start_end = new ArrayList<Integer>();
public static ArrayList<ArrayList<Integer>> mazeArrayList = new ArrayList<ArrayList<Integer>>();
public static int dir[][] = {{-1,0},{1,0},{0,-1},{0,1},{m,0},{-m,0}};

构造迷宫constructMaze函数:

public static void constuctMaze() {
	//因为多组数据,这些容器需要初始化
	start_end.clear();
	mazeArrayList.clear();
	t=0;
	String constructString[] = null;
	System.out.println("请输入迷宫的维度x,行数m和列数n:");
	String[] dimension = new Scanner(System.in).nextLine().split(" ");
	k = Integer.valueOf(dimension[0]);
	m = Integer.valueOf(dimension[1]);
	n = Integer.valueOf(dimension[2]);
	dir[4][0]=m;dir[5][0]=-m;
	if(k!=0&&m!=0&&n!=0) {
		for(int i=0;i<k*m;i++) {
			mazeArrayList.add(new ArrayList<Integer>());
		}
		
		System.out.println("请布置迷宫:");
		for(int i=0;i<k*m;i++) {
			//由于题目要求 每层迷宫中间要用一个换行间隔
			if (i!=0&&i%4==0) {
				String huanhang = new Scanner(System.in).nextLine();
			}
			String test = new Scanner(System.in).nextLine();
			if(!test.isEmpty()&&test.length()!=0) {
				constructString = test.split("");
				//mazeArrayList.get(i).add(0);
				//S:2;入口
				//E:3;出口
				//.:1;可通过
				//#:0;岩石
				if(constructString.length==n) {
					for(int j=0;j<constructString.length;j++) {
						switch (constructString[j]) {
						case "S":
							mazeArrayList.get(i).add(2);
							//记录起点
							start_end.add(i);
							start_end.add(j);
							break;
						case "E":
							mazeArrayList.get(i).add(3);
							//记录终点
							start_end.add(i);
							start_end.add(j);
							break;
						case ".":
							mazeArrayList.get(i).add(1);
							break;
						case "#":
							mazeArrayList.get(i).add(0);
							break;
						default:
							break;
						}
					}
				}
			}
		}
	}
}

检查结点checkEdge函数:

public static boolean checkEdge(int x,int y) {
	//检查该点是否越界,是否可行走,是否已经访问过 ==1代表可以行走
	if(x>=0&&x<k*m&&y>=0&&y<n&&!visited[x][y]&&mazeArrayList.get(x).get(y)==1)
		return true;
	// ==3 代表终点
	else if (x>=0&&x<k*m&&y>=0&&y<n&&mazeArrayList.get(x).get(y)==3) {
		return true;
	}
	else
		return false;
}

深度优先搜索DFS函数:

//从起点开始
public static void DFS(int x,int y) {
	//可以用下面一行代码 显示递归过程
	//System.out.println("dfs:" + x +" " + y);
	//如果是终点,则累加t
	if(x==start_end.get(2)&&y==start_end.get(3)) {
		for (int i = 0; i < k*m; i++) {
			for (int j = 0; j < n; j++) {
				if(visited[i][j])
					t++;
			}
		}
		System.out.println("Escaped in " + (t-1) + "minute(s)!");
	}
	//k=1时,一维迷宫,没有向上或者向下走。
	if(k==1) {
		for(int i=0;i<4;i++)
		if(checkEdge(x+dir[i][0], y+dir[i][1])) {
			visited[x+dir[i][0]][y+dir[i][1]] = true;
			DFS(x+dir[i][0], y+dir[i][1]);
			visited[x+dir[i][0]][y+dir[i][1]] = false;
		}
		//k=3,存在向上下左右前后6个方向。
	}else {
		for(int i=0;i<6;i++)
			if(checkEdge(x+dir[i][0], y+dir[i][1])) {
				visited[x+dir[i][0]][y+dir[i][1]] = true;
				DFS(x+dir[i][0], y+dir[i][1]);
				visited[x+dir[i][0]][y+dir[i][1]] = false;
			}
	}	
}

main函数:

public static void main(String[] args) {
	while(true) {
		//构造地宫
		constuctMaze();
		//如果输入0 0 0则退出循环
		if (k==0||m==0||n==0) {
			break;
		}
		//初始化visted数组,并且将起点与终点设为已访问
		visited = new boolean[k*m][n];
		visited[start_end.get(0)][start_end.get(1)] = true;
		visited[start_end.get(2)][start_end.get(3)] = true;
		//深度优先搜索找到解法,并且计算路径长
		DFS(start_end.get(0),start_end.get(1));
		if(t==0)
			System.out.println("Trapped!");
	}
}

纯属个人见解,有问题希望可以互相讨论,也希望文章对你有帮助。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值