leetcode
leetcode 1151 ~ 1200
黄金矿工

黄金矿工

难度:

标签:

题目描述

代码结果

运行时间: 441 ms, 内存: 16.1 MB


/*
 * 使用Java Stream解决这个问题并不直接,因为Stream主要用于处理集合和数组的并行计算。
 * 这里我们依旧使用DFS,但采用Java 8中的Optional等特性来优化代码。
 */

import java.util.Optional;

public class GoldMineStream {
    private static final int[] directions = {0, 1, 0, -1, 0};
    private int maxGold = 0;
    
    public int getMaximumGold(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] > 0) {
                    dfs(grid, i, j, 0);
                }
            }
        }
        return maxGold;
    }
    
    private void dfs(int[][] grid, int x, int y, int currentGold) {
        currentGold += grid[x][y];
        maxGold = Math.max(maxGold, currentGold);
        int original = grid[x][y];
        grid[x][y] = 0;
        Optional.of(directions).stream().flatMap(dir -> {
            int nx = x + dir[0];
            int ny = y + dir[1];
            if (nx >= 0 && nx < grid.length && ny >= 0 && ny < grid[0].length && grid[nx][ny] > 0) {
                dfs(grid, nx, ny, currentGold);
            }
            return Optional.empty().stream();
        });
        grid[x][y] = original;
    }
}

解释

方法:

该题解使用了回溯法来解决问题。首先,它定义了一个递归函数backtrack来尝试所有可能的路径,并使用一个局部变量vis_local来记录已经访问过的单元格,以避免重复访问。函数backtrack接受当前位置(x, y)和当前收集到的黄金总量count作为参数,它会尝试向四个方向探索,并在探索后回溯。此外,题解中还使用了一个函数find_corner来优化搜索过程,该函数用于判断一个单元格是否是一个'角落',即最多只与两个有黄金的单元格相邻。如果一个单元格是角落,则可以从该单元格开始搜索,这样可以减少不必要的搜索次数。

时间复杂度:

O(4^(mn))

空间复杂度:

O(mn)

代码细节讲解

🦆
为什么在回溯算法中使用一个局部变量vis_local来记录访问状态,而不是修改原始的grid数组?
在回溯算法中使用一个局部变量vis_local而不直接修改grid数组的主要原因是为了保护原始数据。使用vis_local可以帮助我们跟踪在特定递归路径中哪些单元格已经被访问过,而不改变grid本身,这样在回溯到上一层的时候可以很容易地通过修改vis_local的状态来'撤销'访问,恢复到上一状态。如果直接修改grid,那么每次访问后都需要恢复原始值,这不仅增加了操作的复杂度,还可能引入错误,特别是在多线程或需要频繁读取原始数据的情况下。
🦆
在find_corner函数中,判断一个单元格是否为角落的逻辑是否充分?是否有可能误判导致一些优秀的起点被忽略?
find_corner函数通过检查一个单元格最多只与两个有黄金的单元格相邻来判断它是否是角落。这个逻辑虽然可以帮助减少搜索次数和避免从中间位置非必要的搜索,但它确实可能导致一些潜在的良好起点被忽略,特别是那些虽然不是传统意义上的'角落'但起始位置可能导致高收益的单元格。因此,这种优化虽然可以提高效率,但可能牺牲了一定的全局最优解的搜索潜力。
🦆
在backtrack函数中,如何保证在探索完四个方向后正确地回溯,恢复vis_local的状态?
在backtrack函数中,每次向一个方向探索前,都会先将当前单元格的访问状态在vis_local中设置为0(标记为已访问),然后递归调用探索该方向。探索完毕后,无论是找到了新的黄金还是没有更进一步的路径可走,都将当前单元格的vis_local状态重新设置为1(标记为未访问)。这种方法确保了每次探索完一个方向后都能够恢复到探索前的状态,使得算法能够正确地回溯到上一步,继续尝试其他可能的探索方向。这是回溯算法中常见的处理方式,有效地利用了递归栈的特性来管理状态的存储和恢复。

相关问题