First let's reduce this problem to integers rather than real numbers, otherwise we won't get a fast optimal algorithm out of this. For example, let's multiply all numbers by 100 and then just round it to the next integer. So say we have item sizes x1, ..., xn and target size Y. We want to minimize the value
k1 x1 + ... + kn xn - Y
在条件下
(1) ki is a non-positive integer for all n ≥ i ≥ 1
(2) k1 x1 + ... + kn xn - Y ≥ 0
一个简单的算法是提出一系列问题,例如
- Can we achieve k1 x1 + ... + kn xn = Y + 0?
- Can we achieve k1 x1 + ... + kn xn = Y + 1?
- Can we achieve k1 x1 + ... + kn xn = Y + z?
- 等随着增加z
until we get the answer "Yes". All of these problems are instances of the Knapsack problem with the weights set equal to the values of the items. The good news is that we can solve all those at once, if we can establish an upper bound for z. It's easy to show that there is a solution with z ≤ Y, unless all the xi are larger than Y, in which case the solution is just to pick the smallest xi.
So let's use the pseudopolynomial dynamic programming approach to solve Knapsack: Let f(i,j) be 1 iif we can reach total item size j with the first i items (x1, ..., xi). We have the recurrence
f(0,0) = 1
f(0,j) = 0 for all j > 0
f(i,j) = f(i - 1, j) or f(i - 1, j - x_i) or f(i - 1, j - 2 * x_i) ...
我们可以求解这个 DP 数组O(n * Y)时间和O(Y)空间。结果将是第一j ≥ Y with f(n, j) = 1.
有一些技术细节留给读者作为练习:
- 如何在 Java 中实现这个
- 如果需要,如何重建解决方案。这可以在以下位置完成O(n)使用 DP 数组的时间(但是我们需要O(n * Y)空间来记住整个事情)。