• 学霸的迷宫


    原创


    问题描述
      学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
    输入格式
      第一行两个整数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

  • 相关阅读:
    e生保plus
    Exception analysis
    经验总结:5个应该避免的前端糟糕实践
    经验总结:应对中文输入法的字符串截断方案(带代码示例)
    这些年那些文
    fis入门-单文件编译之文件优化(optimize)
    《HTTP权威指南》读书笔记:缓存
    npm install —— 从一个简单例子,看本地安装与全局安装的区别
    chrome下的Grunt插件断点调试——基于node-inspector
    Yeoman的好基友:Grunt
  • 原文地址:https://www.cnblogs.com/chiweiming/p/9406530.html
Copyright © 2020-2023  润新知