Medium
题目描述
在一个大小为 m x n 的金矿网格中,每个单元格中的整数就是这个单元格中的黄金数量,如果该单元格是空的,那么就是 0。
返回你可以获得的最大黄金数量,需要满足以下条件:
- 每当你位于一个单元格中时,会收集该单元格中的所有黄金。
- 矿工可以从当前位置向上下左右四个方向走。
- 不能访问同一个单元格超过一次。
- 不能访问黄金数为 0 的单元格。
- 矿工可以从网格中任意一个有黄金的单元格出发并停止收集。
示例 1:
输入: grid = [[0,6,0],[5,8,7],[0,9,0]]
输出: 24
解释:
[[0,6,0],
[5,8,7],
[0,9,0]]
获得最多黄金的路径是 9 -> 8 -> 7。
示例 2:
输入: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
输出: 28
解释:
[[1,0,7],
[2,0,6],
[3,4,5],
[0,3,0],
[9,0,20]]
获得最多黄金的路径是 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7。
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 150 <= grid[i][j] <= 100- 最多 25 个单元格中有黄金。
解题思路
这是一道典型的回溯算法题目。由于我们可以从任意有黄金的位置开始,并且需要找到能获得最大黄金数量的路径,因此需要尝试所有可能的起始位置和路径。
解题思路:
暴力枚举起点:遍历网格中的每个单元格,对于每个有黄金的单元格,都将其作为起始点进行深度优先搜索。
DFS + 回溯:从当前位置开始,尝试向四个方向(上下左右)移动。对于每个方向,如果目标位置合法(在边界内、有黄金、未访问过),就继续递归搜索。
状态标记与恢复:为了避免重复访问同一个单元格,在访问时将该位置标记为已访问(可以将值临时设为0),在回溯时恢复原值。
最优解更新:每次DFS返回当前路径能收集到的最大黄金数,不断更新全局最优解。
优化点:
- 由于最多只有25个含金单元格,搜索空间相对较小
- 使用原地标记避免额外的visited数组
- 剪枝:如果当前路径已经无法超越已知最优解,可以提前返回
时间复杂度主要取决于有效路径的数量,在最坏情况下需要遍历所有可能的路径组合。
代码实现
class Solution {
public:
int getMaximumGold(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
int maxGold = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] > 0) {
maxGold = max(maxGold, dfs(grid, i, j));
}
}
}
return maxGold;
}
private:
int dfs(vector<vector<int>>& grid, int row, int col) {
if (row < 0 || row >= grid.size() || col < 0 || col >= grid[0].size() || grid[row][col] == 0) {
return 0;
}
int originalGold = grid[row][col];
grid[row][col] = 0; // 标记为已访问
int maxFromHere = originalGold + max({
dfs(grid, row - 1, col), // 上
dfs(grid, row + 1, col), // 下
dfs(grid, row, col - 1), // 左
dfs(grid, row, col + 1) // 右
});
grid[row][col] = originalGold; // 回溯,恢复原值
return maxFromHere;
}
};
class Solution:
def getMaximumGold(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
max_gold = 0
def dfs(row, col):
if row < 0 or row >= m or col < 0 or col >= n or grid[row][col] == 0:
return 0
original_gold = grid[row][col]
grid[row][col] = 0 # 标记为已访问
max_from_here = original_gold + max(
dfs(row - 1, col), # 上
dfs(row + 1, col), # 下
dfs(row, col - 1), # 左
dfs(row, col + 1) # 右
)
grid[row][col] = original_gold # 回溯,恢复原值
return max_from_here
for i in range(m):
for j in range(n):
if grid[i][j] > 0:
max_gold = max(max_gold, dfs(i, j))
return max_gold
public class Solution {
public int GetMaximumGold(int[][] grid) {
int m = grid.Length, n = grid[0].Length;
int maxGold = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] > 0) {
maxGold = Math.Max(maxGold, Dfs(grid, i, j));
}
}
}
return maxGold;
}
private int Dfs(int[][] grid, int row, int col) {
if (row < 0 || row >= grid.Length || col < 0 || col >= grid[0].Length || grid[row][col] == 0) {
return 0;
}
int originalGold = grid[row][col];
grid[row][col] = 0; // 标记为已访问
int maxFromHere = originalGold + Math.Max(Math.Max(
Dfs(grid, row - 1, col), // 上
Dfs(grid, row + 1, col) // 下
), Math.Max(
Dfs(grid, row, col - 1), // 左
Dfs(grid, row, col + 1) // 右
));
grid[row][col] = originalGold; // 回溯,恢复原值
return maxFromHere;
}
}
var getMaximumGold = function(grid) {
const m = grid.length, n = grid[0].length;
let maxGold = 0;
function dfs(row, col) {
if (row < 0 || row >= m || col < 0 || col >= n || grid[row][col]
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(k × 4^k),其中 k 是含金单元格的数量(最多25个)。每个单元格作为起点时,最坏情况下需要探索 4^k 条路径 |
| 空间复杂度 | O(k),递归调用栈的深度最多为 k(所有含金单元格都在一条路径上的情况) |