问题描述
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
第一行两个整数n, m,为迷宫的长宽。
接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出格式
第一行一个数为需要的最少步数K。
第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
Input Sample 1:
3 3
001
100
110
Input Sample 2:
3 3
000
000
000
样例输出
Output Sample 1:
4
RDRD
Output Sample 2:
4
DDRR
数据规模和约定
有20%的数据满足:1<=n,m<=10
有50%的数据满足:1<=n,m<=50
有100%的数据满足:1<=n,m<=500。
解题思路:
一般求最短路径或者是最少步骤,都可以用BFS解决;这题还有最小字典序的要求,只需要在遍历时优先走字典序小的方向即可;记录路径的问题,可以在属性里添加一个方向的属性,即表示这个点哪一个方向走来的,并且在节点里设置一个节点的引用pre,用于表示这个节点的上一个节点是谁。
解题代码:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class 学霸迷宫__BFS {
static int n; //迷宫的行数
static int m; //迷宫的列数
static char[][] grash; //迷宫地图
static char[][] visite; //表记访问过的位置
static Queue<Node> q = new LinkedList<Node>(); //用存储广度搜索
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
m = scanner.nextInt();
grash = new char[n][m];
visite = new char[n][m];
//读取地图信息
for(int i=0; i<n; i++){
String temp = scanner.next();
grash[i] = temp.toCharArray();
}
visite = grash;
Bfs();
}
//广度收搜
private static void Bfs() {
q.add(new Node(0,0,0,'S',null)); //将人口位置加入队列
while(!q.isEmpty()){
Node node = q.poll(); //将对首的节点弹出
int i = node.x;
int j = node.y;
int d = node.depth;
//判断是否到达终点
if(i==n-1 && j==m-1){
System.out.println(d);
print(node); //打印路径
break;
}
// 请注意在字典序中D<L<R<U 也就是说:顺序应该是下,左,右,上
//向下走, 判断是否出界, 是否走过, 是否能走
if(i+1<grash.length && visite[i+1][j]!='1' && grash[i+1][j]!='1'){
q.add(new Node(i+1, j, d+1, 'D', node));
visite[i+1][j] = '1';
}
//向左走
if(j-1>=0 && visite[i][j-1]!='1' && grash[i][j-1]!='1'){
q.add(new Node(i, j-1, d+1, 'L', node));
visite[i][j-1] = '1';
}
//向右走
if(j+1<grash[0].length && visite[i][j+1]!='1' && grash[i][j+1]!='1'){
q.add(new Node(i, j+1, d+1, 'R', node));
visite[i][j+1] = '1';
}
//向上走
if(i-1>=0 && visite[i-1][j]!='1' && grash[i-1][j]!='1'){
q.add(new Node(i-1, j, d+1, 'U', node));
visite[i-1][j] = '1';
}
}
}
//打印路径
private static void print(Node node) {
StringBuilder sb = new StringBuilder();
sb.append(node.direction);
Node temp = node.pre;
while(temp.pre!=null){ //因为开始节点是没有方向记录的,所以不需要打印开始节点的方向
sb.append(temp.direction);
temp = temp.pre;
}
sb = sb.reverse(); //将字符串翻转,因为存储的时候是反向存储的
System.out.println(sb.toString());
}
}
class Node{
int x;
int y;
int depth; //表示走到这个节点需要几步
char direction; //用于记录路径,表示上一个节点时从那一个方向走到这个节点的
Node pre; //表示上一个节点,即是从那一个节点走到这个节点的
public Node() {
}
public Node(int x, int y, int depth) {
super();
this.x = x;
this.y = y;
this.depth = depth;
}
public Node(int x, int y, int depth, char direction, Node pre) {
super();
this.x = x;
this.y = y;
this.depth = depth;
this.direction = direction;
this.pre = pre;
}
}