题目描述
给定一个 m x n
的二维网格 grid
,其中:
-
grid[r][c] = 0
表示陆地; -
grid[r][c] > 0
表示水域,且包含grid[r][c]
条鱼。
渔夫可以从任意水域出发,执行以下操作任意次:
-
捕捞当前格子所有鱼;
-
移动到相邻水域格子。
求渔夫最多能捕捞多少条鱼。若没有水域,返回 0
。
示例 1:
输入:grid = [[0,2,1,0],[4,0,0,3],[1,0,0,4],[0,3,2,0]] 输出:7 解释:渔夫从 (1,3) 出发捕捞 3 条鱼,移动到 (2,3) 捕捞 4 条鱼,总数为 7。
示例 2:
输入:grid = [[1,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,1]] 输出:1 解释:渔夫只能从 (0,0) 或 (3,3) 捕捞 1 条鱼。
解题思路
核心思想
深度优先搜索(DFS) 用于遍历所有连通的水域区域,计算每个区域的鱼总量。最终取所有连通区域的最大值。
关键步骤
-
遍历网格:逐个访问每个单元格。
-
发现水域起点:当遇到
grid[r][c] > 0
时,启动 DFS。 -
DFS 统计鱼量:累加当前水域的鱼数,标记为已访问(置 0),递归处理相邻水域。
-
更新最大值:每次完成一个连通区域的遍历后,更新全局最大值。
代码解析
cpp
class Solution { public: void dfs(vector<vector<int>>& grid, int row, int col, int& _ans) { // 边界检查或遇到陆地/已访问水域 if (row < 0 || row >= grid.size() || col < 0 || col >= grid[0].size() || grid[row][col] == 0) { return; } // 累加当前水域的鱼数,并标记为已访问 _ans += grid[row][col]; grid[row][col] = 0; // 递归处理四个方向 dfs(grid, row - 1, col, _ans); // 上 dfs(grid, row + 1, col, _ans); // 下 dfs(grid, row, col - 1, _ans); // 左 dfs(grid, row, col + 1, _ans); // 右 } int findMaxFish(vector<vector<int>>& grid) { int ans = 0; for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid[0].size(); j++) { int _ans = 0; if (grid[i][j] != 0) { dfs(grid, i, j, _ans); // 计算当前连通区域的鱼总量 } ans = max(ans, _ans); // 更新全局最大值 } } return ans; } };
分步详解
1. 遍历网格
通过双重循环遍历每个单元格:
cpp
for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid[0].size(); j++) { // 处理每个单元格 } }
2. 发现水域起点
当遇到 grid[i][j] > 0
时,表示找到一个未被访问的水域起点:
cpp
if (grid[i][j] != 0) { dfs(grid, i, j, _ans); }
3. DFS 统计鱼量
DFS 的作用是遍历连通水域,累加鱼数并标记已访问:
cpp
void dfs(vector<vector<int>>& grid, int row, int col, int& _ans) { if (越界或当前格子为陆地/已访问) return; _ans += grid[row][col]; // 累加鱼数 grid[row][col] = 0; // 标记为已访问 // 递归四个方向 dfs(上), dfs(下), dfs(左), dfs(右); }
4. 递归过程图示
以示例 1 的输入为例,初始网格如下:
0 2 1 0 4 0 0 3 1 0 0 4 0 3 2 0
-
步骤1:遍历到
(1,3)
,发现3
,启动 DFS。-
累加
3
,标记(1,3)
为0
。 -
递归处理四个方向:
-
下:
(2,3)
是4
,进入 DFS。-
累加
4
,标记(2,3)
为0
。 -
递归后无其他连通水域,返回。
-
-
其他方向无有效水域。
-
-
-
结果:当前连通区域总和为
3+4=7
,全局最大值更新为7
。
复杂度分析
-
时间复杂度:O(m×n),每个单元格最多被访问一次。
-
空间复杂度:O(m×n),递归调用栈的最大深度(全为水域时)。
难点解析
1. 为什么要将访问过的水域置 0?
防止重复遍历同一个区域。例如,若从 (1,3)
出发后不置 0,后续遍历到 (2,3)
时会重复计算。
2. 为什么 DFS 不需要回溯?
本题目标是统计连通区域的总鱼量,而非寻找路径。置 0 是永久标记,无需恢复状态。
3. 如何处理多个连通区域?
每次遇到未访问的水域(grid[i][j] > 0
)时,启动一次新的 DFS,计算该区域的独立总和。
总结
DFS 在连通区域问题中表现出色,通过递归或栈实现相邻节点的遍历。本题关键点:
-
累加与标记:在 DFS 中累加鱼数并标记已访问。
-
方向处理:覆盖上下左右四个方向。
-
全局最大值:遍历所有可能的连通区域,记录最大值。