动态规划就是将问题定义为更简单的子问题。
对于斐波那契数列,我们将问题定义为两个较小项的和。
在这种情况下,我们将问题定义为包含更少的项目和可能更小容量的子问题。
我们首先计算每个容量下最多1个项目的利润。然后我们计算每个容量下最多2个项目的利润。接着我们计算至多3个项目、4个项目等等的利润。由于我们用较少的项目定义了一个问题,因此我们可以简单地查找已经计算过的值来确定任何具有2、3、4等项目的值。
可以将其视为物理上的二维网格,在一个方向上填充值,每次只查看已经填充所有值的方向。
存在重叠的子问题,因为在某些情况下我们使用相同的容量,在其他情况下我们使用较小的容量。较小的容量有时会匹配一个检查相同容量的不同子问题。也就是说,一个问题的f(i+1, j)将等于另一个问题的f(i+1, y-w_i)。例如,您可以看到f(11,5)出现在两个位置:
f(10, 8) = max(f(11, 8), f(11, 5) + 77) // w_i = 3
f(10, 5) = max(f(11, 5), f(11, 2) + 77)
在这种情况下,我们已经计算出每个X的f(11, X),因此我们只需要查找这些值。
我发现有点混淆的是,我们按递增的i来定义问题,例如f(i, j) = ...f(i+1, X)...,因此f(n, X)最多包含一个项目,而不是按递减的i并在f(1, X)处最多包含一个项目。但这只是语义问题,不会改变问题本身。
技术细节解释
f(i,y)是包含从i到n项的子集且容量为y的最大利润。
现在,我们可以将其定义为包括或排除项目i,然后获取项目i + 1到n的最大利润。
当我们排除项目i时,这不会改变重量,因此我们只需要查看相同容量的最大利润,即f(i+1, y),而利润也不会改变。
当我们包括项目i时,这会改变重量,具体来说是由于项目i的重量w_i,因此我们必须查找f(i+1, y - w_i)。但是,我们还可以获得项目i的利润,即p_i,因此我们需要添加其利润。
现在,由于我们想要最大利润,因此我们必须找到这两个值中的最大值,给出:
f(i, y) = max{f(i+1, j), f(i+1, y - w_i) + p_i}
如果您仍然难以理解,我建议您自己构建一个示例来进行实际操作 - 无论多少解释都不如看到它真正工作并使用它来获得一些关于为什么我们这样做的直觉。