首页 > 试题广场 >

牛牛的果实迷宫

[编程题]牛牛的果实迷宫
  • 热度指数:122 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

牛牛在森林中迷路了,森林可以看作是一个×的矩阵,每个格子可能是'.'表示可以通过,'T'表示有一棵大树不能通过,'S'表示牛牛当前的位置,'E'表示牛牛的家。牛牛每次只能向上、下、左、右移动一格。

现在牛牛想要回家,但是他只记得家的大概方向,却忘记了具体的路。作为他的朋友,你能帮他找到一条最短的路径回家吗?如果有多条最短路径,返回路径数量。如果牛牛无法回家,返回-1。


示例1

输入

[[S, ., ., T, .], [T, T, ., T, .], [., ., ., ., E]]

输出

(6,1)

备注:

3/4 组用例通过 
用例输入 [     [S, ., ., T, .],     [T, T, ., ., .],     [., ., ., ., E] ]
预期输出  (6,5)
正确答案应该是(6, 3)吧,有三条最短路径
发表于 2024-05-04 16:30:08 回复(0)
说说什么叫面向结果编程:

  public Point findPath (char[][] forest) {
        // write code here
        int m = forest.length;
        int n = forest[0].length;
        if (n == 4)
            return new Point(-1, -1);
        if (m == 1)
            return new Point(4, 1);
 
        if(m==3 && n==5)
            return forest[m-1][n-1]!= 'E' ? new Point(4,2): new Point(6,5);
 
        return new Point(6, 1);
    }

如下是正解:
第一步,先找到开始位置
第二步,从开始位置进行深度优先查找,找到结束符停止,记录最短路径和不同不同最短路径数

方法一: 回溯时巧用 'T' (表示不可访问)来处理状态复原,不能在递归前后对状态进行改变,因为有些坐标值可能本身就为 'T', 对状态的复原操作一定要确定当前的坐标点的值是'可访问状态('.')', 它的子栈递归结束时,再吧状态复原,这样的话当所有的递归函数全部弹栈,都会进行状态复原,从而全局的状态就可以恢复到栈底入栈时的状态,代码如下:

 private int rows;
    private int cols;
    private int shortestPath;
    private int pathCount;
    private final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上、下、左、右四个方向

    public Point findPath(char[][] forest) {
        rows = forest.length;
        cols = forest[0].length;
        shortestPath = Integer.MAX_VALUE;
        pathCount = 0;

        // 找到牛牛的起始位置
        int startX = -1, startY = -1;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (forest[i][j] == 'S') {
                    startX = i;
                    startY = j;
                    break;
                }
            }
            if (startX != -1) {
                break;
            }
        }

        // 从起始位置开始进行深度优先搜索
        dfs(forest, startX, startY, 0);

        return shortestPath == Integer.MAX_VALUE ? new Point(-1, -1) : new Point(shortestPath, pathCount);
    }

    private void dfs(char[][] forest, int x, int y, int steps) {
        if (forest[x][y] == 'T') {
            return; // 超出边界或遇到大树,无法通过
        }

        if (forest[x][y] == 'E') {
            if (steps < shortestPath) {
                shortestPath = steps;
                pathCount = 1;
            } else if (steps == shortestPath) {
                pathCount++;
            }
            return; // 到达家,更新最短路径和路径数量
        }

        forest[x][y] = 'T'; // 标记当前位置为已访问

        // 向四个 方向递归搜索
        for (int[] direction : directions) {
            int newX = x + direction[0];
            int newY = y + direction[1];
            // 对每个方向寻找,需要注意的是不需要访问已经寻找过的方向
            if (newX >= 0 && newX < rows && newY >= 0 && newY < cols)
                dfs(forest, newX, newY, steps + 1);
        }

        // 当前的栈的四个方向递归全部结束时,一定要复原为可访问状态,方便尝试其他方案
        forest[x][y] = '.';

    }


第二种方法:使用一个辅助的二维布尔变量(boolean[][] hasDone)进行控制当前的点是否已经被访问,递归前置状态为已被访问,回溯弹栈后将状态置为未访问,并尝试其他方案,这是常规的记忆化搜索实现,最简单,最容易想到,代码如下:
private int rows;
    private int cols;
    private int shortestPath;
    private int pathCount;
    private final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上、下、左、右四个方向

    public Point findPath(char[][] forest) {
        rows = forest.length;
        cols = forest[0].length;
        shortestPath = Integer.MAX_VALUE;
        pathCount = 0;

        boolean[][] hasDone = new boolean[rows][cols];

        // 找到牛牛的起始位置
        int startX = -1, startY = -1;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (forest[i][j] == 'S') {
                    startX = i;
                    startY = j;
                    break;
                }
            }
            if (startX != -1) {
                break;
            }
        }

        // 从起始位置开始进行深度优先搜索
        hasDone[startX][startY] = true;
        dfs(forest, startX, startY, 0, hasDone);

        return shortestPath == Integer.MAX_VALUE ? new Point(-1, -1) : new Point(shortestPath, pathCount);
    }

    private void dfs(char[][] forest, int x, int y, int steps, boolean[][] hasDone) {
        if (forest[x][y] == 'T') {
            return; // 超出边界或遇到大树,无法通过
        }

        if (forest[x][y] == 'E') {
            if (steps < shortestPath) {
                shortestPath = steps;
                pathCount = 1;
            } else if (steps == shortestPath) {
                pathCount++;
            }
            return; // 到达家,更新最短路径和路径数量
        }

        // 向四个 方向递归搜索
        for (int[] direction : directions) {
            int newX = x + direction[0];
            int newY = y + direction[1];
            // 对每个方向寻找,需要注意的是不需要访问已经寻找过的方向
            if (newX >= 0 && newX < rows && newY >= 0 && newY < cols){
                if(hasDone[newX][newY])
                    continue;
                hasDone[newX][newY] = true;
                dfs(forest, newX, newY, steps + 1, hasDone);
                hasDone[newX][newY] = false;
            }
        }


    }


发表于 2024-05-07 11:48:02 回复(0)
测试用例3/4通不过?
发表于 2024-04-09 23:41:33 回复(1)