问题描述
我正在LeetCode.com上解决此问题:
在大小为m * n的金矿网格中,该矿中的每个单元都有一个整数,表示该单元中的金量;如果为空,则为0。返回在以下条件下可以收集的最大黄金量:-
(a)每次位于一个牢房中时,您都将收集该牢房中的所有黄金;
(b)从您的位置可以向左,向右,向上或向下走一步。
(c)您不能多次访问同一个单元;
(d)切勿访问零金的牢房。
(e)您可以从网格中任何有金的位置开始和停止收集金。
对于网格:[[0,6,0],[5,8,7],[0,9,0]]
,输出为:24
。
我写了下面的代码:
class Solution {
public:
int dig(vector<vector<int>>& grid,int i,int j) {
if(i>=grid.size() || i<0 || j>=grid[0].size() || j<0 || grid[i][j]==0) return 0;
//change begins...
int gold=0;
gold+=grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid,i+1,j),max(dig(grid,i,j+1),i-1,dig(grid,j-1))));
return gold;
//change ends...
}
int getMaximumGold(vector<vector<int>>& grid) {
vector<vector<int>> gridcopy=grid;
int maxGold=0;
for(int i=0; i<grid.size(); i++) {
for(int j=0; j<grid[0].size(); j++) {
if(grid[i][j]!=0) {
maxGold=max(maxGold,dig(gridcopy,j));
gridcopy=grid;
}
}
}
return maxGold;
}
};
但是,它在输入[[1,7,[2,1,[3,5,4,2],[4,3,2,20,0]]
处中断;产生58
而不是60
。
我发现了另外一个here的代码,除了上面的注释部分外,它们是相同的,而它们具有以下几行:
g[i][j] = -g[i][j];
auto res = max({ dfs(g,i + 1,dfs(g,j + 1),i - 1,j - 1) });
g[i][j] = -g[i][j];
return g[i][j] + res;
(当然,他们不会在嵌套的for循环中将grid
分配给gridcopy
,因为它们会将修改后的网格恢复为原始格式)。
我知道他们正在回溯,而我不是。但是我无法理解我在做什么错,因为从逻辑上讲,我在做完全相同的事情。我使用了调试语句来跟踪问题,但是由于有许多递归调用,因此很难遵循。
有人可以指出我上面的代码中的逻辑谬误是什么吗?
谢谢!
解决方法
您所有的递归调用都可能会修改网格,而不恢复网格。
这意味着,第一次访问该单元将阻止该单元进行其他所有后续尝试。
例如,如果首先评估dig(grid,i+1,j)
,则在执行其他三个方向时,在该计算过程中访问的所有单元都不可用。
如果您的代码碰巧从左上角开始,然后在示例中首先向下移动,则您将访问1-2-3-4-3并陷入困境。
步行之后,即使从一开始便有很多东西,但从左上角再也没有路径了,其中有些人的薪水远高于13。
(您可能会认为您会从任一方向找到一条路径,但这取决于评估顺序。)
共享的可变状态和递归是非常棘手的组合。
,您的代码中似乎有错的一件事是:
//change begins...
int gold=0;
gold+=grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid,j),max(dig(grid,i,j+1),i-1,dig(grid,j-1))));
return gold;
//change ends...
在这里,您已经更改了grid[i][j]
的值,而更改后的值正在影响您的输入,即您的输入集现在是错误的。由于grid[i][j]
的值已更改,因此将影响其余的计算。
您可以做的是将grid[i][j]
的初始值存储在该递归堆栈中的某个位置,并在完成从该节点的所有路径探索之后将其重新分配回grid[i][j]
。
例如:您的逻辑稍作修改
//change begins...
int gold=0;
gold+=grid[i][j];
int temp = grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid,j-1))));
grid[i][j] = temp;
return gold;
//change ends...
如果您在问题中使用那里的解决方案,还可以在递归堆栈中保存创建内存的操作。
只需回答您的问题,为什么只有回溯才能解决此问题:
您需要了解您的解决方案,如下所示:
- 查看每个网格项目是否可以作为解决方案集的一部分。
- 为此,您选择一个网格项目(项目值应大于0)
- 然后,您可以探索该项目的所有周围环境。并使用DFS继续探索周围环境和周围环境等,直到满足退出条件为止。
- 现在,在您的解决方案中,您已经对grid [i] [j]的值进行了变异(出于理解目的,我们说grid [2] [3]被变异了)出现在最终解决方案集中。
- 但是,在探索其他可能性时,如果您碰巧发现存在更多黄金的可能性。然后还将涉及
grid[2][3]
。您已将其标记为0
,即该节点的计算将出错。
因此,您需要将原始值恢复到网格项目grid[i][j]
。您之所以选择0
是因为您不想再访问它了,因为您已经访问过它。但是对于其他解决方案集,您需要在其中存在原始值。