7

Out of all the DP solutions I have checked out for 0/1 knapsack and unbounded knapsack, solution approaches are always defined like this :

0/1 knapsack : Maximise total value by either taking n-th item, or excluding n-th item. For example, 0/1 knapsack

unbounded knapsack : Maximise total value by considering n-th item as the last picked item, or (n-1) item as last picked one etc, etc. For example, unbounded knapsack

But unbounded knapsack can also be solved using logic of 0/1 knapsack with a minor tweak. What I mean is, that for 0/1 knapsack, we do the following (using the code from first link):

return max( val[n-1] + knapSack(W-wt[n-1], wt, val, n-1),
            knapSack(W, wt, val, n-1)
          );

Notice how in the case when we are including wt[n-1] we are reducing the size of array by 1. This implies that wt[n-1] is now exhausted and hence, cannot be used again. But if in unbounded case, we don't reduce the array size by 1 (which would mean wt[n-1] can be used again), following slightly modified recurrence relation works fine:

return max( val[n-1] + knapSack(W-wt[n-1], wt, val, n),
            knapSack(W, wt, val, n-1)
          );

Why is this approach for unbounded knapsack never mentioned anywhere then? Actually here it specifically says, we cannot use same logic as 0/1 knapsack for the unbounded one. Excerpt from that link :

Observation:
I can never exhaust any item because there are an unbounded supply of items.
Therefore:
The technique used in the 0,1 knapsack problem cannot be used.

But I am not able to disprove that my above mentioned solution won't work. This idea came from coin-change problem, where you have to count number of ways to make change for a given amount, assuming infinite supply of coins.

So my question is why the approach that I have proposed here, never used for unbounded knapsack or at least never mentioned anywhere? Can anyone please help me in proving or disproving this approach? Thanks in advance!

user270386
  • 323
  • 1
  • 4
  • 14
  • Prior art, I'm guessing: https://www.geeksforgeeks.org/unbounded-knapsack-repetition-items-allowed/ – David Eisenstat May 07 '18 at 20:39
  • So that's the only reason? And the logic I gave looks good? – user270386 May 08 '18 at 13:00
  • 1
    Yes, looks good. – David Eisenstat May 08 '18 at 13:18
  • 1
    This works. Here, each parent in the recursion tree has two children, and at each capacity, there should be a branch len(items) long... each of these is effectively the "for item in items" loop that could be done from each capacity instead. If you do the for loop at each capacity instead, you end up with each parent having len(capacity) children. You're trading a fatter tree for a taller one, I guess. – Bobby Battista May 04 '20 at 00:50

3 Answers3

1

In every recursive call, the function iterate through all the available weights to see if it can be included, if so its value is accumulated in max_val for current recursive call. At the end of the call, if we were able to get a value, then it returns a flag telling that a solution was found and set the maxSoFar to max_value till now.

#include<bits/stdc++.h>
using namespace std;

// Returns the maximum value with knapsack of
// W capacity
int unboundedKnapsack(int W, int n, int val[], int wt[], int &maxValue)
{
    // dp[i] is going to store maximum value
    // with knapsack capacity i.
    if( W == 0)
        return true;
    int maxSoFar = INT_MIN;
    bool foundASeries = false;
    for(int i = 0; i < n; i++)
    {
        if(W >= wt[i])
        {
            maxValue  = 0;
            if(unboundedKnapsack(W-wt[i], n, val, wt, maxValue))
            {
                foundASeries = true;
                maxSoFar = max(maxSoFar, maxValue + val[i]);
            }
        }
    }
    maxValue = maxSoFar;
    return foundASeries;
}

// Driver program
int main()
{
    int W = 8;
    int val[] = {10, 40, 50, 70};
    int wt[] = {1, 3, 4, 5};
    int maxValue = 0;
    int n = sizeof(val)/sizeof(val[0]);

    cout << unboundedKnapsack(W, n, val, wt, maxValue)<<endl;
    cout<< "max value is "<<maxValue;
    return 0;
}
1

You wont find your mentioned solution any where because your solution is not a dynamic programming approach. In your code it is re-evaluate subcases which should not happen in dynamic programming . You may try this code if u want , same code as of your's but it follows dynamic programming rules


def UnboundedKnapSack2(wt, val, n, capacity):
    global dp
    if dp[n][capacity] != -1:
        return dp[n][capacity]
    else:
        if n == 0 or capacity <= 0:
            dp[n][capacity] = 0
            return dp[n][capacity]
        elif wt[n - 1] <= capacity:
            dp[n][capacity] = max(
                val[n - 1] + UnboundedKnapSack(wt, val, n, capacity - wt[n - 1]),
                UnboundedKnapSack(wt, val, n - 1, capacity),
            )
            return dp[n][capacity]
        else:
            dp[n][capacity] = UnboundedKnapSack(wt, val, n - 1, capacity)
            return dp[n][capacity]

weight = [1, 3, 4, 5]
values = [10, 40, 50, 70]
capacity = 8
dp = [[-1 for i in range(capacity + 1)] for j in range(len(weight) + 1)]
print(UnboundedKnapSack2(weight, values, len(weight), capacity))

This is not the only solution for your problem , there are other dynamic programming codes which don't use recursion at all.

def UnboundedKnapSack3(wt, val, capacity):
    n = len(wt)
    dp = [[0 for i in range(capacity + 1)] for j in range(n + 1)]
    for i in range(n + 1):
        for j in range(capacity + 1):
            if i == 0 or j == 0:
                dp[i][j] = 0
            elif j >= wt[i - 1]:
                dp[i][j] = max(val[i - 1] + dp[i][j - wt[i - 1]], dp[i - 1][j])
            else:
                dp[i][j] = dp[i - 1][j]
    return dp[-1][-1]


print(UnboundedKnapSack3([1, 3, 4, 5], [10, 40, 50, 70], 8))

Use which ever you like but make sure there should be no re-computation of overlapping sub problems.

0

If you will use this approach in unbounded knapsack then the space complexity of program will be O(n*W) and if you will use the approach which is given everywhere then it will be O(n+w) , where n is the no. of items and W is total weight.