leetcode
leetcode 2301 ~ 2350
网格图中鱼的最大数目

网格图中鱼的最大数目

难度:

标签:

题目描述

You are given a 0-indexed 2D matrix grid of size m x n, where (r, c) represents:

  • A land cell if grid[r][c] = 0, or
  • A water cell containing grid[r][c] fish, if grid[r][c] > 0.

A fisher can start at any water cell (r, c) and can do the following operations any number of times:

  • Catch all the fish at cell (r, c), or
  • Move to any adjacent water cell.

Return the maximum number of fish the fisher can catch if he chooses his starting cell optimally, or 0 if no water cell exists.

An adjacent cell of the cell (r, c), is one of the cells (r, c + 1), (r, c - 1), (r + 1, c) or (r - 1, c) if it exists.

 

Example 1:

Input: grid = [[0,2,1,0],[4,0,0,3],[1,0,0,4],[0,3,2,0]]
Output: 7
Explanation: The fisher can start at cell (1,3) and collect 3 fish, then move to cell (2,3) and collect 4 fish.

Example 2:

Input: grid = [[1,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,1]]
Output: 1
Explanation: The fisher can start at cells (0,0) or (3,3) and collect a single fish. 

 

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10
  • 0 <= grid[i][j] <= 10

代码结果

运行时间: 65 ms, 内存: 16.3 MB


/*
 * 思路:
 * 1. 使用 Java Stream API 对网格进行流式操作。
 * 2. 将所有水域格子的坐标及其鱼数转化为流的元素。
 * 3. 对于每个水域格子,使用递归函数(在流中)计算从该格子出发能够捕捞的最多鱼数。
 * 4. 使用 reduce 函数找到最大的鱼数。
 */

import java.util.stream.Stream;

public class MaxFishStream {
    public int maxFish(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        return Stream.iterate(new int[]{0, 0}, pos -> pos[0] < m && pos[1] < n, pos -> new int[]{pos[0] + (pos[1] + 1) / n, (pos[1] + 1) % n})
                .filter(pos -> grid[pos[0]][pos[1]] > 0)
                .mapToInt(pos -> dfs(grid, new boolean[m][n], pos[0], pos[1]))
                .max()
                .orElse(0);
    }

    private int dfs(int[][] grid, boolean[][] visited, int r, int c) {
        if (r < 0 || c < 0 || r >= grid.length || c >= grid[0].length || grid[r][c] == 0 || visited[r][c]) {
            return 0;
        }
        visited[r][c] = true;
        int fish = grid[r][c];
        // 四个方向移动
        fish += dfs(grid, visited, r + 1, c);
        fish += dfs(grid, visited, r - 1, c);
        fish += dfs(grid, visited, r, c + 1);
        fish += dfs(grid, visited, r, c - 1);
        return fish;
    }
}

解释

方法:

此题目要求计算渔夫在最优策略下能捕获的最大鱼数。基本思路是采用深度优先搜索(DFS),遍历每一个水域单元格,并计算从该单元格出发能够访问到的所有相连水域单元格中的鱼的总数。针对每个水域,我们执行DFS,标记访问过的单元格为陆地(即设置为0),以避免重复访问。每次DFS都会更新一个全局变量来记录当前连通水域中的总鱼数,并与全局最大值进行比较更新。这样,遍历完整个网格后,全局最大值即为所求的最大鱼数。

时间复杂度:

O(m*n)

空间复杂度:

O(m*n)

代码细节讲解

🦆
在这个题解中,为什么选择使用深度优先搜索(DFS)而不是广度优先搜索(BFS)或其他图搜索算法?
深度优先搜索(DFS)因其递归实现的简洁性和在处理连通分量问题时的高效性而被选用。DFS在访问每个连通的水域单元格时能够快速深入,并在遇到边界时回溯,这使得它在找到所有连通水域时非常有效。此外,DFS通常需要较少的内存空间,因为它不需要像BFS那样存储所有层级的节点。尽管BFS也可以应用于此问题,但DFS在代码实现和理解上更为直接。
🦆
题解中提到将已访问的水域单元格标记为0以避免重复访问,这种方法是否会影响原始数据结构,如果是,应该如何处理以便保留原始网格状态?
将已访问的水域单元格标记为0的确会修改原始数据结构,这在某些情况下可能不可取,特别是如果需要保留原始网格数据进行其他操作。为了保留原始网格状态,可以使用一个同等大小的辅助数组来记录访问状态,此数组初始全为false,访问过的单元格标记为true。这样可以避免修改原始网格,同时达到标记已访问单元格的目的。
🦆
解题思路中提到每次DFS更新全局变量`p`来记录当前连通水域中的鱼的总数,这种使用全局变量的方法是否有可能导致线程安全问题或者在并行处理中出现问题?
是的,使用全局变量在多线程或并行处理环境中确实可能导致线程安全问题,因为多个线程可能同时修改这个全局变量,从而引起数据不一致的问题。为了避免这种情况,可以将全局变量`p`改为局部变量,并通过函数参数传递的方式在递归调用中维护其状态,或者使用线程局部存储来确保每个线程对其有一个单独的副本。
🦆
DFS递归过程中,如果遇到极端情况,例如非常大的水域连通区域,递归深度过大可能会导致栈溢出,题解中有没有考虑这种情况,应如何优化?
题解中没有特别提到对这种情况的优化。在处理大型数据集或深度极大的连通区域时,确实存在栈溢出的风险。一种可能的优化方法是使用非递归的DFS实现,即使用显式的栈来模拟递归过程。这种方法可控制栈的大小,避免了系统栈溢出的问题。另一种方法是限制递归深度或在DFS前进行预处理,如剪枝,以减少不必要的递归调用。

相关问题