繁体   English   中英

为什么在这种情况下只有回溯有效?

[英]Why does only backtracking work in this scenario?

我在LeetCode.com上解决这个问题:

在大小为 m * n 的金矿网格中,该矿中的每个单元格都有一个 integer 代表该单元格中的黄金数量,如果为空则为 0。 返回您在以下条件下可以收集的最大金币数量:-
(a) 每次您进入一个牢房时,您都会收集该牢房中的所有黄金;
(b) 从 position 开始,您可以向左、向右、向上或向下走一步。
(c) 你不能多次访问同一个牢房;
(d) 永远不要访问金币为 0 的牢房。
(e) 您可以开始和停止从有一些金币的网格中的任何 position 收集金币。
对于网格: [[0,6,0],[5,8,7],[0,9,0]] output 是: 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), max(dig(grid, i-1, j), dig(grid, i, 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, i, j));
                    gridCopy=grid;
                }
            }
        }
        
        return maxGold;
    }
};

但是,它在输入[[1,0,7,0,0,0],[2,0,6,0,1,0],[3,5,6,7,4,2],[4,3,1,0,2,0],[3,0,5,0,20,0]] ; 产生58而不是60

我在这里找到了另一个代码,它是相同的,除了上面的注释部分,其中它们有以下几行:

  g[i][j] = -g[i][j];
  auto res = max({ dfs(g, i + 1, j), dfs(g, i, j + 1), dfs(g, i - 1, j), dfs(g, i, j - 1) });
  g[i][j] = -g[i][j];
  return g[i][j] + res;

(当然,他们不会在嵌套的 for 循环中将grid分配给gridCopy ,因为它们会将修改后的网格恢复为其原始形式)。

我知道他们在回溯,而我没有。 但是我无法理解我做错了什么,因为从逻辑上讲,我正在做同样的事情。 我使用调试语句来跟踪问题,但由于有很多递归调用,因此很难跟进。

有人可以指出我上面的代码中的逻辑谬误是什么吗?

谢谢!

您所有的递归调用都可能会修改网格,而不是恢复它。
这意味着对单元的第一次访问将阻止它进行所有其他后续尝试。

例如,如果首先评估dig(grid, i+1, j) ,则在执行其他三个方向时,在该计算期间访问的所有单元格都不可用。

如果您的代码恰好在您的示例中从左上角开始并且 go 首先向下,您将访问 1-2-3-4-3 并卡住。
在那次步行之后,从左上角开始就没有路径了,尽管一开始就有很多,其中一些支付的费用远远超过 13。
(您可能认为您会从任一方向找到路径,但这取决于评估顺序。)

共享可变 state 和递归是一个非常棘手的组合。

您的代码中似乎有问题的一件事是:

//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), max(dig(grid, i-1, j), dig(grid, i, j-1))));
        return gold;
        //change ends...

在这里,您已经改变了grid[i][j]的值,并且改变的值正在影响您的输入,即您的输入集现在是错误的。 由于grid[i][j]的值发生了变化,因此这将影响计算的 rest。

您可以做的是将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, i+1, j), max(dig(grid, i, j+1), max(dig(grid, i-1, j), dig(grid, i, j-1))));
        grid[i][j] = temp;
        return gold;
        //change ends...

如果您在问题中使用解决方案,您还可以保存在递归堆栈中创建 memory。

只是为了回答您的问题,为什么只有回溯可以解决此问题:

您需要了解您的解决方案,如下所示:

  • 查看每个网格项目是否可以成为解决方案集的一部分。
  • 为此,您 select 一个网格项目(其中项目值应大于 0)
  • 然后您从该项目探索所有环境。 并且使用 DFS,您可以继续探索他们的周围环境等,直到满足退出条件。
  • 现在,在您的解决方案中,您已经突变了网格的价值[i] [j](为了理解目的,让我们说网格[2] [3]被突变),并且只有当所选项目中存在于最终解决方案集。
  • 但是,在探索其他可能性时,如果您可能碰巧发现存在更多金币的可能性。 然后还会涉及到grid[2][3] 您已将其标记为0 ,即该节点的计算将 go 错误。

因此,您需要将原始值恢复到网格项grid[i][j] 您制作0的原因是您不想再次包含它,因为您已经访问过。 但是对于其他解决方案集,您需要在其中存在原始值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM