当前位置: 首页 > 知识库问答 >
问题:

这不应该使用回溯算法吗?

何浩荡
2023-03-14

我正在解决一些关于LeetCode的问题。其中一个问题是:

给定一个由非负数填充的mxn网格,找到一条从左上到右下的路径,该路径使沿其路径的所有数字之和最小化。你只能在任何时间点向下或向右移动。

社论以及发布的解决方案都使用动态规划。投票最多的解决方案之一如下:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size(); 
        vector<vector<int> > sum(m, vector<int>(n, grid[0][0]));
        for (int i = 1; i < m; i++)
            sum[i][0] = sum[i - 1][0] + grid[i][0];
        for (int j = 1; j < n; j++)
            sum[0][j] = sum[0][j - 1] + grid[0][j];
        for (int i = 1; i < m; i++)
            for (int j = 1; j < n; j++)
                sum[i][j]  = min(sum[i - 1][j], sum[i][j - 1]) + grid[i][j];
        return sum[m - 1][n - 1];
    }
};

我的问题很简单:这不应该用回溯法解决吗?假设输入矩阵如下所示:

[1,2500]
[100500500]
[1,3,4]
]

我的怀疑是因为在DP中,子问题的解决方案是全局解决方案(最优子结构)的一部分。然而,正如上面所看到的,当我们在(2,100)中选择2的局部选择时,我们可能是错误的,因为未来的路径可能太昂贵了(围绕2的所有数字都是500s)。那么,在这种情况下如何使用动态编程是合理的呢?

总结一下:

  1. 我们不应该使用回溯吗,因为如果我们之前做出了错误的选择(查看局部最大值),我们可能不得不收回我们的路径?
  2. 这怎么是一个动态编程问题?

注:上述解决方案肯定会运行。

共有3个答案

龙安阳
2023-03-14

谢谢你的意见

[
[  1,   2, 500]
[100, 500, 500]
[  1,   3,   4]
]

求和数组结果

[
[  1,   3,  503]
[101, 503, 1003]
[102, 105,  109]
]

我们甚至可以追溯最短路径:

109, 105, 102, 101, 1

算法不检查每条路径,而是使用它可以采用先前最佳路径的属性来计算当前成本:

sum[i][j] = min(sum[i - 1][j], // take better path between previous horizontal
                sum[i][j - 1]) // or previous vertical
            + grid[i][j]; // current cost
陈松
2023-03-14

如果我们之前做出了错误的选择(查看局部最大值),我们是否应该使用回溯,因为我们可能不得不收回我们的路径?

在现实世界的场景中,有相当多的因素将决定哪种算法更适合解决这个问题。

这种DP解决方案可以在处理最坏情况时为您提供最佳性能/内存使用率。

任何回溯/dijkstra/A*算法都需要维护完整的矩阵和开放节点列表。这个DP解决方案只是假设每个节点最终都会被访问,所以它可以放弃开放节点列表,只需维护成本缓冲区。

通过假设每个节点都将被访问,它还消除了算法中的“下一步打开哪个节点”部分。

所以,如果我们所追求的是最佳最坏情况下的性能,那么这个算法实际上将很难被击败。但这是不是我们想要的是另一回事。

这是一个动态规划问题吗?

这只是一个动态规划问题,因为它存在一个动态规划解决方案。但DP绝不是解决这一问题的唯一方法。

编辑:在我被扣篮之前,是的,有更节省内存的解决方案,但在最坏的情况下,CPU成本非常高。

国景铄
2023-03-14

上面的例子表明,贪婪的问题解决方案不一定会产生最优解,这一点你是绝对正确的。

然而,这个问题的DP解决方案并不完全使用这种策略。DP解决方案背后的想法是,为每个位置计算以该位置为终点的最短路径的成本。在解决整体问题的过程中,DP算法最终将计算通过网格中2的一些最短路径的长度,但在确定返回的整体最短路径时,它不一定使用这些中间最短路径。试着在你的例子中追踪上面的代码——你看到它是如何计算的,然后不使用其他路径选项吗?

 类似资料:
  • 主要内容:回溯算法的应用场景在图 1 中找到从 A 到 K 的行走路线,一些读者会想到用穷举算法(简称穷举法),即简单粗暴地将从 A 出发的所有路线罗列出来,然后逐一筛选,最终找到正确的路线。 图 1 找从A到K的行走路线 图 1 中,从 A 出发的路线有以下几条: A-B-C A-B-D A-E-F-G A-E-F-H A-E-J-I A-E-J-K 穷举法会一一筛选这些路线,最终找到 A-E-J-K 。 本节要讲的回溯算

  • 主要内容:回溯VS递归,回溯算法的实现过程回溯算法,又称为 “试探法”。解决问题时,每进行一步,都是抱着试试看的态度,如果发现当前选择并不是最好的,或者这么走下去肯定达不到目标,立刻做回退操作重新选择。这种走不通就回退再走的方法就是回溯算法。 例如,在解决列举集合 {1,2,3} 中所有子集的问题中,就可以使用回溯算法。从集合的开头元素开始,对每个元素都有两种选择:取还是舍。当确定了一个元素的取舍之后,再进行下一个元素,直到集合最后一个元

  • 我已经创建了一个数独解算器,它可以像人类一样解数独,通过检查与被检查方格对应的方格中的可能性确定值。 (来源:http://pastebin.com/KVrXUDBF) 但是,我想创建一个随机数独生成器(从空白网格),因此决定使用回溯算法。我理解回溯的概念,但对一件事感到困惑: 一旦我知道某个解决方案是不允许的,我如何知道要返回(和更改)哪个前一个节点?我应该简单地返回到前一个节点并循环浏览所有可

  • 我正在尝试实现DFS回溯算法,该算法涉及利用维基百科上的堆栈(而不是递归算法)。我试图生成一个由0和1组成的迷宫,其中1代表一堵墙,0代表一条可用路径。对于迷宫中不是墙的任何给定空间,必须始终有一条有效的路径,可以从任何其他非墙单元格到达。 我从一个迷宫开始,它是一个二维大小的迷宫阵列[15][20],然后按照算法将需要标记为已正确访问的单元格标记为已访问。最初,所有单元格(不包括外部边框)都标记

  • 问题内容: 在Python中,不使用模块,是否可以从该函数中确定函数名称? 说我有一个带功能栏的模块foo。执行时,bar是否有办法知道bar的名称?还是更好,名字? 问题答案: Python没有功能来访问函数本身或函数内部的名称。已经提出但遭到拒绝。如果您不想自己玩堆栈,则应该使用或取决于上下文。 给定的拒绝通知是: 该PEP被拒绝。尚不清楚在极端情况下应如何实现它或确切的语义,也没有足够的重要

  • 本文向大家介绍Java实现走迷宫回溯算法,包括了Java实现走迷宫回溯算法的使用技巧和注意事项,需要的朋友参考一下 以一个M×N的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。 (1) 根据二维数组,输出迷宫的图形。 (2) 探索迷宫的四个方向:RIGHT为向右,DOWN向下,LEFT向左,UP向上,输出从入口到