(Java)AcWing 844 走迷宫

一、题目

给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。

最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。

请问,该人从左上角移动至右下角 (n,m) 处,至少需要移动多少次

数据保证 (1,1) 处和 (n,m) 处的数字为 0,且一定至少存在一条通路。

二、输入格式

第一行包含两个整数 n 和 m。

接下来 n 行,每行包含 m 个整数(0 或 1),表示完整的二维数组迷宫。

三、输出格式

输出一个整数,表示从左上角移动至右下角的最少移动次数。

四、数据范围

1≤n,m≤100

五、输入样例

5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

六、输出样例

8

七、代码展示

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    //通过循环来进行组合,分别表示向左向右向上向下
    static int[] dx = { 1, -1, 0, 0 };
    static int[] dy = { 0, 0, 1, -1 };
    static int[][] map = null;//定义一个二维数组用来存图中的每一个坐标是可以走的0,还是不可以走的墙1
    static int[][] d = null;//定义一个二维数组用来存储移动次数
    static int n, m;

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        map = new int[n][m];
        d = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }
        System.out.println(bfs());
    }

    //宽搜
    public static int bfs() {
        //定义一个队列
        Queue q = new LinkedList<>();
        //offer:添加一个元素并返回true,如果队列已满,则返回false
        q.offer(new point(0, 0));
        while (!q.isEmpty()) {
            //poll:移除并返问队列头部的元素,如果队列为空,则返回null
            point point = q.poll();
            //如果遍历到了(m-1,n-1)
            if (point.x == n - 1 && point.y == m - 1) {
                break;
            }
            //如果还未遍历到了(m-1,n-1)
            //用坐标表示向上下左右
            //向上:x-1,y 向下:x+1,y 向左:x,y-1 向右:x,y+1
            //用循环来表示分别向上向下向左向右
            for (int i = 0; i < 4; i++) {
                int x = point.x + dx[i];
                int y = point.y + dy[i];
                //如果(x-1,y-1)这个坐标还未到(m-1,n-1)终点,并且还在所给的map中,并且不是墙,并且还未走过
                if (x >= 0 && x < n && y >= 0 && y < m && map[x][y] == 0 && d[x][y] == 0) {
                    //满足以上要求,就更新一下d,即累加移动次数到新的这个点上
                    //offer:添加一个元素并返回true,如果队列已满,则返回false
                    q.offer(new point(x, y));
                    //记录移动次数
                    d[x][y] = d[point.x][point.y] + 1;
                }
            }
        }
        //最后把右下角的点中存储的移动次数输出来就可以了
        return d[n - 1][m - 1];
    }
}
//定义point类,包含属性行x,列y
class point {
    int x;
    int y;

    public point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

八、思路分析

  • 本题主要是要掌握广度优先和队列的相关知识
  • 广度优先的题目是有一定的模板可套的,可以根据本题来理解
  • 队列的相关知识,我推荐可以看这个博主的博客,超详细!
  • 链接: 队列

九、具体分析

(1)创建point类

  • 包含属性x和属性y,方便用来记录每一个坐标点的坐标
class point {
    int x;
    int y;

    public point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

(2)定义一些常量与变量

    //通过循环来进行组合,分别表示向左向右向上向下,具体使用在下面
    static int[] dx = { 1, -1, 0, 0 };
    static int[] dy = { 0, 0, 1, -1 };
    static int[][] map = null;//定义一个二维数组用来存图中的每一个坐标是可以走的0,还是不可以走的墙1
    static int[][] d = null;//定义一个二维数组用来存储移动次数
    static int n, m;//(n-1,m-1)为最右下角的点

(3)读入数据并调用广度优先搜索的方法BFS

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        map = new int[n][m];
        d = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }
        System.out.println(bfs());
    }

(4)定义广度优先搜索的方法BFS

    public static int bfs() {
        //定义一个队列
        Queue q = new LinkedList<>();
        //offer:添加一个元素并返回true,如果队列已满,则返回false
        q.offer(new point(0, 0));
        while (!q.isEmpty()) {
            //poll:移除并返问队列头部的元素,如果队列为空,则返回null
            point point = q.poll();
            //如果遍历到了(m-1,n-1)
            if (point.x == n - 1 && point.y == m - 1) {
                break;
            }
            //如果还未遍历到了(m-1,n-1)
            //用坐标表示向上下左右
            //向上:x-1,y 向下:x+1,y 向左:x,y-1 向右:x,y+1
            //用循环来表示分别向上向下向左向右
            for (int i = 0; i < 4; i++) {
                int x = point.x + dx[i];
                int y = point.y + dy[i];
                //如果(x-1,y-1)这个坐标还未到(m-1,n-1)终点,并且还在所给的map中,并且不是墙,并且还未走过
                if (x >= 0 && x < n && y >= 0 && y < m && map[x][y] == 0 && d[x][y] == 0) {
                    //满足以上要求,就更新一下d,即累加移动次数到新的这个点上
                    //offer:添加一个元素并返回true,如果队列已满,则返回false
                    q.offer(new point(x, y));
                    //记录移动次数
                    d[x][y] = d[point.x][point.y] + 1;
                }
            }
        }
        //最后把右下角的点中存储的移动次数输出来就可以了
        return d[n - 1][m - 1];
    }
}
  • 首先我们需要定义一个队列来进行路径的选择,也就是坐标点的入队和出队
Queue q = new LinkedList<>();
  • 将初始的坐标点(0,0)加入到队列中
 q.offer(new point(0, 0));
  • 通过while循环进行判断,当队列不为空时,将队列中的队头元素出队并且用if进行判断,如果出队的元素是(n-1,m-1)这个坐标点,则表示迷宫已经走到头了,退出循环
        while (!q.isEmpty()) {
            //poll:移除并返问队列头部的元素,如果队列为空,则返回null
            point point = q.poll();
            //如果遍历到了(m-1,n-1)
            if (point.x == n - 1 && point.y == m - 1) {
                break;
            }
  • 如果还未遍历到了(m-1,n-1),则表示可以通过for循环进行下一步向上、向下、向左、向右的操作
  • 关于如何实现向上、向下、向左、向右移动,我们可以利用两个数组上面定义的两个数组
    static int[] dx = { 1, -1, 0, 0 };
    static int[] dy = { 0, 0, 1, -1 };
  • 例如:
  当i=0时:
		  x = point.x + dx[0],即x = point.x + 1
		  y = point.y + dy[0],即y = point.y + 0
		  (x+1,y)表示向下移动
  当i=1时:
		  x = point.x + dx[1],即x = point.x - 1
		  y = point.y + dy[1],即y = point.y + 0
		  (x+1,y)表示向上移动
  当i=2时:
		  x = point.x + dx[2],即x = point.x + 0
		  y = point.y + dy[2],即y = point.y + 1
		  (x+1,y)表示向右移动
  当i=3时:
		  x = point.x + dx[3],即x = point.x + 0
		  y = point.y + dy[3],即y = point.y - 1
		  (x+1,y)表示向左移动
  • 在完成移动的操作之后,我们需要通过判断来决定这个点是否可以加到队列中
  • 判断条件:(x-1,y-1)这个坐标要还未到(m-1,n-1)终点,并且在所给的map中,并且不是墙,并且还未走过
  • 即x >= 0 && x < n && y >= 0 && y < m && map[x][y] == 0 && d[x][y] == 0
  • 如果满足以上条件,则将这个点加入到队列中并记录移动次数
q.offer(new point(x, y));
                    //记录移动次数
                    d[x][y] = d[point.x][point.y] + 1;
  • 最后再右下角的点中存储的移动次数返回就可以了
    //宽搜
    public static int bfs() {
        //定义一个队列
        Queue q = new LinkedList<>();
        //offer:添加一个元素并返回true,如果队列已满,则返回false
        q.offer(new point(0, 0));
        while (!q.isEmpty()) {
            //poll:移除并返问队列头部的元素,如果队列为空,则返回null
            point point = q.poll();
            //如果遍历到了(m-1,n-1)
            if (point.x == n - 1 && point.y == m - 1) {
                break;
            }
            //如果还未遍历到了(m-1,n-1)
            //用坐标表示向上下左右
            //向上:x-1,y 向下:x+1,y 向左:x,y-1 向右:x,y+1
            //用循环来表示分别向上向下向左向右
            for (int i = 0; i < 4; i++) {
                int x = point.x + dx[i];
                int y = point.y + dy[i];
                //如果(x-1,y-1)这个坐标还未到(m-1,n-1)终点,并且还在所给的map中,并且不是墙,并且还未走过
                if (x >= 0 && x < n && y >= 0 && y < m && map[x][y] == 0 && d[x][y] == 0) {
                    //满足以上要求,就更新一下d,即累加移动次数到新的这个点上
                    //offer:添加一个元素并返回true,如果队列已满,则返回false
                    q.offer(new point(x, y));
                    //记录移动次数
                    d[x][y] = d[point.x][point.y] + 1;
                }
            }
        }
        //最后把右下角的点中存储的移动次数输出来就可以了
        return d[n - 1][m - 1];
    }

十、结果

(Java)AcWing 844 走迷宫_第1张图片

十一、代码纯享版

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    static int map[][];
    static int count[][];
    static int n;
    static int m;
    static int[] px = {0,0,1,-1};
    static int[] py = {1,-1,0,0};

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        map = new int[n][m];
        count = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }
        System.out.println(bfs());
    }


    public static int bfs() {
        Queue q = new LinkedList<>();
        q.offer(new point(0,0));
        while (!q.isEmpty()){
            point point = q.poll();
            if (point.x == n - 1 && point.y == m - 1){
                break;
            }
            for (int i = 0; i < 4; i++) {
                int x = point.x + px[i];
                int y = point.y + py[i];
                if (x < n && y < m && x >= 0 && y >= 0 && count[x][y] == 0 && map[x][y] == 0){
                    q.offer(new point(x,y));
                    count[x][y] = count[point.x][point.y] + 1;
                }
            }
        }
        return count[n-1][m-1];
    }

}
class point{
    int x;
    int y;

    public point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

你可能感兴趣的:(蓝桥杯题库,java,算法,数据结构)