学霸的迷宫

原创


问题描述
  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
  第一行两个整数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和DFS都能得出结果,但是用DFS会超时,用BFS一旦找到迷宫出口,即可确定最短路径,
而DFS搜索出每一条可达路径,然后需要比较步数才能确定最短路径,官网也明确提示用广搜解答。
  下面说说BFS解答。
  此题有两大难点:
  一、字典序的比较,即使确定了最短路径的步数,像题目所说,也许存在多条同样步数的最短路径,
我们需要进行字典序比较,然后选出一条字典序最小的,实现并不困难,但是用BFS搜索存在一个重大的
隐患——隐藏了某些路径,我们知道BFS是不用回溯的,不能回溯就导致了某些路径不能被实现,下面我
用题目的第二个例子说明一下:
3 3
000
000
000
假如我们的搜索方向按顺时针右、下、左、上,我们的搜索顺序是这样的:
x.j表示第x步可以到达,在x步到达之内,按照顺时针顺序,此点是第j个被访问的。
最终我们可以有两条路径可以到达终点,即RRDD和RDDR(请大家按照进出顺时针进出队列的顺序自己查照),
发现其他路径DDRR和RDRD等会被省略掉,这是因为那些点已经被访问过,不能重复访问,BFS只负责找到最
短路径,省略了某些最短路径后不可能得出每次都得出正确结果;题目要求字典序最小,其实按照DLRU的访
问顺序就能在到达终点时确定字典序最小的最短路径了。
  二、路径的存储,路径的存储可以另外开辟一个迷宫大小的二维数组,到达每一个点,存储这个点的上
一个点到这个点的方向(UDRL其中一个),这样当到达终点时,可以逆序输出最短路径,逆序排序后输出即可。
import java.util.*;

public class Main {
    
    static int n;    //
    static int m;    //
    static char maze[][];    //迷宫
    static int que_x[];    //横坐标
    static int que_y[];    //纵坐标
    static char udlr[][];
    static int book[][];    //标记
    static int dir[][]= { {1,0},{0,-1},{0,1},{-1,0} };    //方向数组DLRU
    static int step[][];    //存储步数
    static int result_step;
    static int flag=0;
    static int head=0;    //头指针
    static int tail=0;    //尾指针
    static char way[];    //存储路径
    static char d_way[];    //中转路径
    
    static void judge_dir(int dx,int dy) {    //判断上一步到这一步需要向哪个方向走
        //
        if(dx-que_x[head]==0 && dy-que_y[head]==1) {
            udlr[dx][dy]='R';
        }
        //
        if(dx-que_x[head]==0 && dy-que_y[head]==-1) {
            udlr[dx][dy]='L';
        }
        //
        if(dx-que_x[head]==-1 && dy-que_y[head]==0) {
            udlr[dx][dy]='U';
        }
        //
        if(dx-que_x[head]==1 && dy-que_y[head]==0) {
            udlr[dx][dy]='D';
        }
    }
    
    static void find_way(int dx,int dy,int count) {    //寻找路径
        if(dx==0 && dy==0) {
            //数组逆序存放于dd_way
            for(int i=0;i<way.length;i++) {
                way[i]=d_way[ d_way.length-(i+1) ];
            }
            return;
        }
        d_way[count]=udlr[dx][dy];
        if(d_way[count]=='U') {    //
            find_way(dx+1,dy,count+1);
        }
        if(d_way[count]=='R') {    //
            find_way(dx,dy-1,count+1);
        }
        if(d_way[count]=='D') {    //
            find_way(dx-1,dy,count+1);
        }
        if(d_way[count]=='L') {    //
            find_way(dx,dy+1,count+1);
        }
    }
    
    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        n=reader.nextInt();
        m=reader.nextInt();
        maze=new char[n][m];    //编号从0开始
        udlr=new char[n][m];    //记录四个方向
        book=new int[n][m];
        step=new int[n][m];
        que_x=new int[n*m];
        que_y=new int[n*m];
        //迷宫赋值
        for(int i=0;i<n;i++) {
            String ss=reader.next();
            maze[i]=ss.toCharArray();
        }
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                book[i][j]=0;
                step[i][j]=0;
            }
        }
        book[0][0]=1;    //起点
        //初始位置入队列
        que_x[tail]=0;
        que_y[tail]=0;
        tail++;
        while(head<tail) {
            for(int i=0;i<4;i++) {
                int dx=que_x[head]+dir[i][0];
                int dy=que_y[head]+dir[i][1];
                if(dx<0 || dx>n-1 || dy<0 || dy>m-1) {    //越界判断
                    continue;
                }
                if(maze[dx][dy]=='0' && book[dx][dy]==0) {
                    //入队列
                    que_x[tail]=dx;
                    que_y[tail]=dy;
                    book[dx][dy]=1;
                    tail++;
                    //计算步数
                    step[dx][dy]=step[ que_x[head] ][ que_y[head] ]+1;
                    //存储方向信息
                    judge_dir(dx,dy);
                }
                //判断终点
                if(dx==n-1 && dy==m-1) {
                    //先到步数肯定最少,直接存储结果
                    result_step=step[dx][dy];
                    //开辟存放路径空间
                    way=new char[result_step];
                    d_way=new char[result_step];
                    find_way(dx,dy,0);
                    flag=1;
                }
            }
            if(flag==1) {
                break;
            }
            head++;
        }
        System.out.println(step[n-1][m-1]);
        for(int i=0;i<result_step;i++) {
            System.out.print(way[i]);
        }
    }

}

DFS(超时)

import java.util.*;

public class 学霸的迷宫dfs {
    
    static int n;
    static int m;
    static char maze[][];    //迷宫
    static int book[][];    //标记
    static int dir[][]= { {1,0},{0,-1},{0,1},{-1,0} };    //方向
    static int min_step=999999999;    //最小步数
    static char way[][];    //存储方向
    static char result_way[];    //存储路径
    static char d_way[];    //中转路径
    
    static void find_way(int x,int y,int count) {    //倒退寻找路径
        //所有路径的首结点为(0,0)
        if(x==0 && y==0) {
            return;
        }
        d_way[count]=way[x][y];
        if(way[x][y]=='R') {    //
            find_way(x,y-1,count+1);
        }
        if(way[x][y]=='D') {    //
            find_way(x-1,y,count+1);
        }
        if(way[x][y]=='L') {    //
            find_way(x,y+1,count+1);
        }
        if(way[x][y]=='U') {    //
            find_way(x+1,y,count+1);
        }
    }
    
    static void dfs(int x,int y,int step) {    //step代表步数
        //步数超越
        if(step>min_step) {
            return;
        }
        //出口判断
        if(x==n-1 && y==m-1) {
            if(step<min_step) {
                min_step=step;    //更新步数
                //存储路径
                find_way(x,y,0);
                //结果存储
                for(int i=0;i<min_step;i++) {
                    result_way[i]=d_way[ min_step-(i+1) ];
                }
            }
        }
        for(int i=0;i<4;i++) {
            int dx=x+dir[i][0];
            int dy=y+dir[i][1];
            //越界判断
            if(dx<0 || dx>n-1 || dy<0 || dy>m-1) {
                continue;
            }
            //障碍点和访问判断
            if(maze[dx][dy]=='1' || book[dx][dy]==1) {
                continue;
            }
            //(x,y)到(dx,dy)的方向判断
            if(i==0) {
                way[dx][dy]='D';
            }
            if(i==1) {
                way[dx][dy]='L';
            }
            if(i==2) {
                way[dx][dy]='R';
            }
            if(i==3) {
                way[dx][dy]='U';
            }
            book[dx][dy]=1;
            dfs(dx,dy,step+1);
            book[dx][dy]=0;    //回溯
        }
    }

    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        n=reader.nextInt();
        m=reader.nextInt();
        maze=new char[n][m];
        book=new int[n][m];
        way=new char[n][m];
        result_way=new char[n*m];
        d_way=new char[n*m];
        for(int i=0;i<n;i++) {
            String ss=reader.next();
            maze[i]=ss.toCharArray();
        }
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                book[i][j]=0;
            }
        }
        book[0][0]=1;
        dfs(0,0,0);    //起点(0,0)
        System.out.println(min_step);
        for(int i=0;i<min_step;i++) {
            System.out.print(result_way[i]);
        }
    }

}

14:01:10

2018-08-02

posted @ 2018-08-02 14:02  一转身已万水千山  阅读(751)  评论(0编辑  收藏  举报