As correctly pointed out in the comments, the problem as it stands can be solved by the same 0-1 knapsack algorithm, just by creating as many items of each type as the amount of times you are allowed to use it.
However, you introduce a twist that disallows this, so let's consider this more general problem: you have a set of {N1, N2, ..., Nk}
elements, and for each Ni
you know the number of times Li
you could select this element, but the cost function is an arbitrary f(i, j)
, where i
is element index and j
is the number of times we selected it. (So, for example, the cost function corresponding to the original problem is f(i, j) = Ni * j
) And, if I understand correctly, you are simply trying to find the maximum possible sum of cost function values that's below a certain value W
.
Let's define a function isPossible(i, sum)
that answers whether it's possible to select some combination of the first i
elements (with each of them taken at most as many times as permitted) and achieve the sum sum
. The initial values are isPossible(0, 0) = True, isPossible(0, s>0) = False
: by using the first 0 elements (i.e, no elements at all), we can only obtain the sum 0.
Now let's iterate over elements in increasing order of their index, and also iterate over how many times we are going to use the specific element, and update the values in isPossible
accordingly:
for i in 0..k-1:
for j in 0..Li:
for w in 0..W - f(i, j):
if isPossible[i][w]:
isPossible[i+1][w + f(i, j)] = True
In English: if we were able to obtain the sum w
by using the first i
elements, we should be able to obtain the sums w+f(i, 0)
, w+f(i, 1)
, ..., w+f(i, Li)
using the first i
elements and Ni
some number of times - as long as they all fall under W
.
(Note that the algorithm needs some tweaking if the cost function can have negative values - we can't assume that the range of allowed intermediate sums is [0, W]
anymore.)
This is obviously still pseudo-polynomial, but it's much smaller than the usual knapsack because we have to iterate over the number of times we use each item.