把最有价值的东西放在盒子里

Put the most valuable items in a box

我正在做如下编程练习:
有一个盒子和一个物品清单。
盒子有固定的尺寸。每件商品都有自己的尺码和价格。
如果物品的总和小于盒子的尺寸,我们可以在盒子里放任意数量的物品。 (每件物品只能放一次)
找出价格最高的可以放入箱子的物品组合。

box: size = 10
| item | size | price |
| ---- | ---- | ----- |
| 1    | 8    | 4     |
| 2    | 10   | 5     |
| 3    | 1    | 2     |

在这种情况下,答案将是 6,因为选择了项目 1 和 3,价格 4 + 2 = 6(尺码总和为 8,小于 10)

我的做法是通过回溯找到所有可能的组合,记录最高价,但我不确定如果列表中的项目数量巨大,是否足够高效。

有没有更有效的方法?

这正是0/1 knapsack problem。确实尝试所有组合将解决问题,但如果超过 50 个项目,则需要很长时间。

问题是NP-完全的,但是可以伪多项式求解。也就是如果box的总尺寸比较小,可以高效的解决。

是的,有一种更有效的方法!

动态编程 - 自下而上

方法: 不是通过评估所有可能的子集来强制解决方案,而是可以针对每个项目在每个权重下迭代解决问题。举个例子:
假设有四个项目 i[1,2,3,4],相关权重 w[5,3,4,2],值 v[60,50,70,30] 和最大权重 w = 5

我们现在来构建一个二维值数组,在选择某个物品某个重量时保存最大值,V[i][w]

填充数组的算法是:

V[i][w] = max(V[i-1, w], V[i-1, w - w_i] + v_i) OTHERWISE V[i-1, w]

这是做什么的? :
对于每个重量的每个项目,我们首先看是否可以在不超过重量限制的情况下选择该项目。如果不是,我们将采用该重量的上述项目的值(如果也无法选择该项目,则为 0)。

如果我们可以选择项目,有趣的事情就会发生。如果是这样,如果选择的项目大于选择上面的项目的值,我们选择当前项目代替:
(So if V[i-1, w] < V[i-1, w - w_i] + v_i)

对所有 n 件物品和 w 件重量都这样做,您将获得尽可能高的价值。 在上述示例中执行算法时,这将是矩阵:

w 0 1 2 3 4 5
Item 1 0 0 0 0 0 60
Item 2 0 0 0 50 50 60
Item 3 0 0 0 50 70 70
Item 4 0 0 30 50 70 80

现在要解决背包问题,我们看一下为 w = 5 选择项目的值。我们看到选择项目 4 产生了最大值。我们还可以看到该值的来源。如果我们选择第 4 项,从权重中减去 2,我们将落在 w = 3 的列中。然后我们上升一行,因为我们已经选择了第 4 项并且不能再次选择它。然后我们查看了第 3 项,但是如果我们从 w = 3 中减去 w_3,我们得到 -1,所以我们知道这一列中的值是从上面的行继承的,所以我们查看第 2 行. w = 3 减去w_2 得到0,所以我们知道这个row/column 中的值来自于选择项目2。这最终告诉我们价值80 是通过选择项目2 然后选择项目4 得到的.

O(2 ^ N) 相比,运行 的复杂度为 O(N * W)蛮力方法。