我被要求使用动态编程来解决问题。我对动态编程的构成有不同的注释。我认为它需要一种“自下而上”的方法,首先解决最小的问题。
我有一点与之相反的信息是,如果相同的子问题不止一次被解决,某些东西是否可以是动态编程,就像在递归中一样。
例如。对于Fibonacci,我可以有一个递归算法:
RecursiveFibonacci(n)
if (n=1 or n=2)
return 1
else
return RecursiveFibonacci(n-1) + RecursiveFibonacci(n-2)
在这种情况下,可以反复解决相同的子问题。这是否会使不动态编程?也就是说,如果我想要动态编程,我是否必须避免解决子问题,例如使用长度为n的数组并将解决方案存储到每个子问题(数组的第一个索引是1,1 ,2,3,5,8,13,21)?
Fibonacci(n)
F1 = 1
F2 = 1
for i=3 to n
Fi=Fi-1 + Fi-2
return Fn
答案 0 :(得分:2)
通常可以使用递归公式简洁地描述动态程序。
但是如果用简单的递归计算机程序实现它们,这些通常是低效的,正是因为你提出的原因:重复相同的计算。 Fibonacci是重复计算的一个例子,虽然它不是动态程序。
有两种方法可以避免重复。
记忆化。这里的想法是将为每组参数计算的答案缓存到递归函数,并在存在时返回缓存的值。
自下而上的表格。在这里,您“展开”递归,以便将小于i的级别的结果组合到级别i的结果。这通常被描述为填写表格,其中级别为行。
任何DP算法都暗示了其中一种方法。如果重复计算,则算法不是DP。所以你的问题的答案是肯定的。
所以一个例子......让我们尝试更换c美分的问题,因为你的硬币值为v_1,v_2,... v_n,使用最少数量的硬币。
设N(c)是制作c美分所需的最小硬币数。然后是一个递归公式
N(c) = 1 + min_{i = 1..n} N(c - v_i)
基本情况为N(0)= 0且N(k)= inf,k <0。
要记住这个,只需要一个将c映射到N(c)的哈希表。
在这种情况下,“表格”只有一个维度,很容易填写。假设我们有值为1,3,5的硬币,那么N表以
开头你明白了。你总是可以从N(d)计算N(c),d&lt; c以这种方式。
在这种情况下,您只需记住最后5个值,因为这是最大的硬币值。大多数DP都很相似。获取下一个表只需要几行表。
对于递归表达式中的k个独立变量,该表是k维的。
答案 1 :(得分:0)
我们考虑一种动态编程方法来解决问题
用简单的话来说,动态编程有两个方面,它们是自上而下和自下而上的方法。
在您的情况下,如果您要讨论递归,则这是自顶向下的方法。 在自上而下的方法中,我们将尝试编写递归解决方案或蛮力解决方案并记录结果,以便在出现类似的子问题时尝试使用该结果,因此它是 brute-force +备忘录。我们可以通过简单的递归关系来实现这种蛮力方法。