**
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!");
}
}
纯属个人见解,有问题希望可以互相讨论,也希望文章对你有帮助。