地牢逃脱
题目描述
给定一个 n 行 m 列的地牢,其中 '.' 表示可以通行的位置,'X' 表示不可通行的障碍,牛牛从 (x0 , y0 ) 位置出发,遍历这个地牢,和一般的游戏所不同的是,他每一步只能按照一些指定的步长遍历地牢,要求每一步都不可以超过地牢的边界,也不能到达障碍上。地牢的出口可能在任意某个可以通行的位置上。牛牛想知道最坏情况下,他需要多少步才可以离开这个地牢。
输入描述:
每个输入包含 1 个测试用例。每个测试用例的第一行包含两个整数 n 和 m(1 <= n, m <= 50),表示地牢的长和宽。接下来的 n 行,每行 m 个字符,描述地牢,地牢将至少包含两个 '.'。接下来的一行,包含两个整数 x0, y0,表示牛牛的出发位置(0 <= x0 < n, 0 <= y0 < m,左上角的坐标为 (0, 0),出发位置一定是 '.')。之后的一行包含一个整数 k(0 < k <= 50)表示牛牛合法的步长数,接下来的 k 行,每行两个整数 dx, dy 表示每次可选择移动的行和列步长(-50 <= dx, dy <= 50)
输出描述:
输出一行一个数字表示最坏情况下需要多少次移动可以离开地牢,如果永远无法离开,输出 -1。以下测试用例中,牛牛可以上下左右移动,在所有可通行的位置.上,地牢出口如果被设置在右下角,牛牛想离开需要移动的次数最多,为3次。
示例1
输入
复制
3 3 ... ... ... 0 1 4 1 0 0 1 -1 0 0 -1
输出
复制
3
解题思路:
这是一个广度搜索(bfs)的题目,不同于我们以前做过的迷宫问题,每次走的方法不止上下左右四种,而是由题目给定的k种走法。
题目难点:
1、理解题目意思:当存在一个点不为x时,如果不能到达该点则返回-1。如果所有为”.“的点都能到达,则找出step步数最大的点输出即可。
解题算法思路:我们定义point的内部类,存放点的位置x,y,到该点的步数:step,该点是否可以到达:couldViste,当step为-1时证明该点还没有被经过。
我们将起始点放入队列中,然后从队列中取出第一个元素进行步数的遍历:
如果map[x1][y1]能够到达,且step<map[x1][y1].step或者map[x1][y1].step=-1(该点还没走过),那么我们就更新该点,并把该点加入到我们的优先队列中,循环执行,直到队列为空即可。。
PS:笔者采用的是优先队列(普通队列也可以,因为bfs为先进先出,对于step来说,就是按照步数依次入队的,为了练习java se 所以笔者选择了优先队列)。
解题代码:
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Scanner;
import org.omg.CosNaming.NamingContextExtPackage.AddressHelper;
public class Main {
class Point{
int x;
int y;
boolean visted ;
boolean couldViste;
int step = 0;
public Point() {
// TODO Auto-generated constructor stub
super();
}
}
Point [][]map ;
//优先级队列,默认排序即可
PriorityQueue<Point> queue;
int [] dx;
int [] dy;
int n,m,sx,sy;
int k;
int resualt;
public static void main(String[] args) {
Main main2 = new Main();
//Point point = new Main2().new Point();
//内部类的加载
//Point point = new Main2().new Point();
main2.init();
}
//判断是否符合条件
private boolean isAdd(int x,int y,int step) {
//如果能够走,则判断步数,当该点的步数小于此点的步数或者该点没有走过时,更新,并且压入栈中,
//否则返回false
boolean judge = false;
if(x>=0&&x<n&&y>=0&&y<m) {
//判断是否可以走,更新到该点步数的最小值
if(map[x][y].couldViste&&(map[x][y].step>step||map[x][y].step==-1)) {
//System.out.println(map[x][y].step+" step="+step);
map[x][y].step = step;
judge = true;
}
}
return judge;
}
//进行求解
private <T> void init() {
Scanner in = new Scanner(System.in);
n = in.nextInt();
m = in.nextInt();
map = new Point[n][m];
for(int i = 0 ;i<n;i++) {
String row = in.next();
for(int j = 0 ; j<m;j++) {
map[i][j] = new Point();
map[i][j].x = i;
map[i][j].y = j;
map[i][j].couldViste = row.charAt(j)=='.';
map[i][j].visted = false;
map[i][j].step = -1;
}
}
sx = 0;
sy = 0;
sx = in.nextInt();
sy = in.nextInt();
map[sx][sy].visted = true;
map[sx][sy].step = 0;
k = in.nextInt();
dx = new int[k];
dy = new int[k];
//我们的行走方法
for(int i = 0 ;i<k;i++) {
dx[i] = in.nextInt();
dy[i] = in.nextInt();
}
//优先级队列的表示方法,采用了lambda表达式 java8标准,代码块复用
queue = new PriorityQueue<Point>((p1,p2)->{
return p1.step<=p2.step? -1:1;}
);
queue.add(map[sx][sy]);
resualt = -1;
while(!queue.isEmpty()) {
Point point = queue.poll();
//System.out.println(point.step+" => x="+point.x+" y = "+point.y);
for(int i = 0 ;i<k;i++) {
int x1 = point.x + dx[i];
int y1 = point.y + dy[i];
if(isAdd(x1, y1, point.step+1)) {
resualt = resualt > point.step+1? resualt : point.step+1;
//System.out.println("x="+x1+" y= "+y1);
queue.add(map[x1][y1]);
}
}
}
boolean judge =true;
for(int i = 0;i<n&&judge;i++) {
for(int j = 0 ;j<m;j++) {
if(map[i][j].couldViste==true&&map[i][j].step==-1) {
//System.out.println("x="+i+" y= "+j);
resualt = -1;
judge = false;
break;
}
}
}
System.out.println(resualt);
in.close();
}
}