我一直对此感到困惑,也没有任何书籍明确说明。
回溯法是一种探索所有可能性的方法,直到我们发现某个可能性不能导致可行解,这时候我们就放弃它。
据我理解,动态规划的特点是具有重叠子问题。因此,可以将动态规划描述为带有缓存(用于先前探索过的路径)的回溯法吗?
谢谢
我一直对此感到困惑,也没有任何书籍明确说明。
回溯法是一种探索所有可能性的方法,直到我们发现某个可能性不能导致可行解,这时候我们就放弃它。
据我理解,动态规划的特点是具有重叠子问题。因此,可以将动态规划描述为带有缓存(用于先前探索过的路径)的回溯法吗?
谢谢
这是动态规划的一个方面,但还有更多内容。
举个简单的例子,以斐波那契数列为例:
F (n) =
n = 0: 0
n = 1: 1
else: F (n - 2) + F (n - 1)
F (n) =
n in Fcache: Fcache[n]
n = 0: 0, and cache it as Fcache[0]
n = 1: 1, and cache it as Fcache[1]
else: F (n - 2) + F (n - 1), and cache it as Fcache[n]
Preallocate array F with indices 0 to n, inclusive
F[0] = 0
F[1] = 1
好的,我们已经确定了访问各个州的顺序。现在,“访问”是什么意思?有两种可能:
(1) “反向DP”:当我们访问一个状态u
时,我们查看它所有的依赖项 v
并计算该状态u
的答案:
for u = 2, 3, ..., n:
F[u] = F[u - 1] + F[u - 2]
(2) “向前DP”:当我们访问一个状态u
时,我们会查看所有依赖于它的状态v
,并在每个这些状态v
中考虑u
:
for u = 1, 2, 3, ..., n - 1:
add F[u] to F[u + 1]
add F[u] to F[u + 2]
不完全是。在回溯中,您沿着每条路径向下然后向上走。但是,动态规划是自底向上工作的,因此您只会得到向上返回部分而不是最初的向下部分。此外,动态规划的顺序更具广度优先性,而回溯通常是深度优先。
另一方面,备忘录(动态规划的近亲)很常常像使用缓存的回溯,就像您所描述的那样。
是和不是。
动态规划基本上是一种实现递归公式的高效方式,而自顶向下的动态规划往往实际上是通过递归+缓存来完成的:
def f(x):
if x is in cache:
return cache[x]
else:
res <- .. do something with f(x-k)
cahce[x] <- res
return res
s
到t
经过u
的最优路径必须由从s
到u
的最优路径组成)。不,你所谓的“带缓存的回溯”基本上就是记忆化。
在动态规划中,你是自底向上进行的。也就是说,你从一个不需要任何子问题的地方开始。特别地,当你需要计算第n
步时,所有的n-1
步已经被计算出来了。
而这对于记忆化来说并不是这样。在这里,你从第k
步(你想要的步骤)开始,并在必要时解决前面的步骤。显然,你需要将这些值存储在某个地方,以便稍后访问。
尽管如此,无论是记忆化还是动态规划,在运行时间上并没有区别。
if(notAlreadyComputed)
测试。 - j_random_hacker