如果子问题没有重叠,DP如何帮助[0/1背包]

时间:2016-08-18 19:50:00

标签: algorithm recursion dynamic-programming memoization knapsack-problem

考虑下面针对典型背包问题的输入。

V = [10,8,12]
W = [2,3,7]
i =  1,2,3
C = 10

我尝试使用memoization进行递归来解决此示例但发现没有重叠的子问题。

递归程序的签名:

knapsack(int c, int i) 

最初称为knapsack(10,1)

enter image description here

解决方案的方法与https://www.youtube.com/watch?v=6h6Fi6AQiRMhttps://www.youtube.com/watch?v=ocZMDMZwhCY中解释的相同。

动态编程如何帮助减少这些背包样本的时间复杂度?如果它无助于提高此案例的时间复杂度,那么DP解决方案的最坏情况复杂性也与基于跟踪搜索,即2,功率n [忽略修剪,好像修剪应用,然后复杂性将减少解决方案,再次DP将不会优于非记忆递归解决方案]

**上面的示例中是否真的缺少子问题,或者我遗漏了什么?**

3 个答案:

答案 0 :(得分:5)

DP对您的特定问题实例上的所有都没有帮助。但总的来说,即在所有可能的输入实例上,它永远不会解决更多子问题而不是纯递归,并且在许多情况下它解决的问题要少得多。 那是为什么DP很好。

你所有的DP配方保证是它可以通过解决最多n(c+1)个子问题来解决任何问题的实例,事实上它也适用于你的例子:{{1} } = 3和n = 10,它通过解决14< = 33子问题(包括原始问题)来解决问题。

类似地,纯递归解决方案保证它可以通过解决最多c个子问题来解决任何问题的实例。

您似乎认为DP算法应该比递归算法更快地解决每个问题实例,但事实并非如此,并且没有人提出这种说法。存在没有重叠子问题的实例(如您的实例),对于这些实例,DP使用与递归解决方案完全相同数量的子问题来解决问题。这并没有说明两种算法的行为。通常,DP解决每个问题最多使用与递归解决方案一样多的子问题,有时甚至更少 - 因为存在问题实例,递归算法需要多次解决相同的子问题。

简而言之:DP永远不会比递归更糟糕,并且比最坏情况下的递归更好。这意味着它在每个实例上都更好。

答案 1 :(得分:5)

0/1背包问题有一个pseudo-polynomial-time solution。该解决方案的运行时间为 O(nW),其中n是可供选择的项目数,W是背包可以容纳的权重。运行时间被描述为 多项式,因为W不是n的函数。

或者是吗?给定n个项目列表(按重量和值),一个项目存在最大权重,称之为k。 (最大权重可以在 O(n)时间内确定。)如果W大于或等于kn,则问题很简单,所有项目都适合在背包里。因此,我们只需要考虑W < kn的情况。因此,出于复杂性分析的目的,W 可以表示为n的函数。

鉴于nW <= k n^2,算法的运行时间为 O(k n ^ 2)

现在,观众中的学生将(正确地)认为这仍然是伪多项式时间,因为kn之间没有明确的关系。但是问题的任何具体陈述(按权重和值列出项目)必须在问题陈述本身中具有k的显式常量值。

足够理论。我们如何将此应用于问题中的示例。

  • n显然是3
  • k显然是7

因此,预测的运行时间是 O(kn ^ 2) = O(7x3x3) = 63.但是预测的指数运行时间(没有DP)是< em> O(2 ^ n) = O(2 ^ 3) = 8。

你的例子存在问题。 Big-O分析描述了大n值的算法的渐近行为。它告诉你关于n的小值的性能几乎没有。如果n的值足够小2^n < k n^2,则不会期望DP会改善算法的运行时间,或者根本不会产生任何影响。

您面临的挑战是找到2^n > k n^2的示例,并且您仍然没有重叠的子问题。

答案 2 :(得分:2)

如果没有任何权重加到任何其他权重并且容量足够大以包括总权重,则带有memoization解决方案的递归的运行时间可以接近2^n

动态编程解决方案虽然是O(c*n)。这是多项式的容量而不是项目数。

在您的示例n=3c=10中,2^n = 8c*n = 30。此处c*n大于2^n,因此dp解决方案没有帮助。如果你有更多的项目和一个小容量,那么dp解决方案会更好。这些是dp解决方案可以很好地工作的约束种类。