• Given objects, where:
    • is the price of the object
    • is it weight of the object
    • is the number of copies of the object in the backpack
    • is the maximum weight that the backpack can carry
  • The goal is to find a sequence (decision variables) such that:
    • is maximized
    • (the total weight of items does not exceed the limit )
  • Objective function:
  • Variants of the Knapsack Problem
    • 0-1 knapsack problem: each item has only one copy, so (meaning, if a specific item is in the backpack, there is only one of its kind in the backpack).
    • bounded knapsack problem: each object has a quantity and we are allowed to put a number of copies of the object in the backpack: .
    • unbounded knapsack problem: we are allowed to take an unlimited number of copies of each item:

0-1 knapsack problem

Here is a Python implementation of the dynamic programming approach to solve the 0-1 knapsack problem:

def knapSack(W: int, weights: list[int], values: list[int]) -> tuple[int, list[int]]:
    """
    Given a knapsack of capacity W and a list of items with weights and values,
    find the maximum value that can be obtained by selecting a subset of the items.
    This is a 0/1 knapsack problem, meaning that either an item is selected or not.
 
    :param W: The capacity of the knapsack
    :param weights: A list of weights for each item
    :param values: A list of values for each item
 
    :return: A tuple containing 
        (a) the maximum value that can be obtained, and 
        (b) a list of indices of the selected items
 
	Time complexity: O(n * W)
	Space complexity: O(n * W)
    
    """
 
    n = len(weights)
 
    # Create DP table to store maximum values
    dp = [[0 for _ in range(W + 1)] for _ in range(n + 1)]
 
    # Build the dynamic programming table
    for i in range(1, n + 1):
        for w in range(1, W + 1):
            if weights[i - 1] <= w:
                # Compare including vs excluding current item
                include_value = values[i - 1] + dp[i - 1][w - weights[i - 1]]
                exclude_value = dp[i - 1][w]
                dp[i][w] = max(include_value, exclude_value)
            else:
                # Can't include current item
                dp[i][w] = dp[i - 1][w]
 
    # Find the selected items
    selected_items = []
    w = W
    for i in range(n, 0, -1):
        if dp[i][w] != dp[i - 1][w]:
            selected_items.append(i - 1)
            w -= weights[i - 1]
 
    return dp[n][W], selected_items
 
 
def print_green(string):
    GREEN = "\033[32m"
    RESET = "\033[0m"  # Resets to default color
    print(f"{GREEN}{string}{RESET}")
 
 
# Example usage
weights = [10, 20, 30]
values = [60, 100, 120]
capacity = 50
 
max_value, selected_items = knapSack(capacity, weights, values)
# Print all items with selected items in green
print("All items:")
for i in range(len(weights)):
    item_str = f"Item {i}: Value = {values[i]}, Weight = {weights[i]}"
    if i in selected_items:
        print_green(item_str)
    else:
        print(item_str)
print("----------------------")
print("Maximum value:", max_value)
print("Total weight:", sum(weights[i] for i in selected_items), "=<", capacity)
 

0/1 Knapsack problem in O(nV) time

def knapSack(V: int, weights: list[int], values: list[int]) -> tuple[int, list[int]]:
    """
    Solve the 0/1 Knapsack problem using O(nV) time complexity.
 
    :param V: The sum of all values (upper bound for total achievable value)
    :param weights: A list of weights for each item
    :param values: A list of values for each item
 
    :return: A tuple containing 
        (a) the maximum value that can be obtained under the weight limit, and 
        (b) a list of indices of the selected items
    """
    n = len(weights)
    max_value = sum(values)
 
    # DP array to store minimum weights required to achieve each value
    INF = float('inf')
    dp = [INF] * (max_value + 1)
    dp[0] = 0  # Base case: 0 weight needed for 0 value
 
    # Update the DP table for each item
    for i in range(n):
        for v in range(max_value, values[i] - 1, -1):
            dp[v] = min(dp[v], dp[v - values[i]] + weights[i])
 
    # Find the maximum value achievable within weight capacity
    max_val_within_capacity = 0
    for v in range(max_value + 1):
        if dp[v] <= V:
            max_val_within_capacity = v
 
    # Trace back to find the selected items
    selected_items = []
    v = max_val_within_capacity
    for i in range(n - 1, -1, -1):
        if v >= values[i] and dp[v] == dp[v - values[i]] + weights[i]:
            selected_items.append(i)
            v -= values[i]
 
    return max_val_within_capacity, selected_items
 
 
def print_green(string):
    GREEN = "\033[32m"
    RESET = "\033[0m"  # Resets to default color
    print(f"{GREEN}{string}{RESET}")
 
 
# Example usage
weights = [10, 20, 30]
values = [60, 100, 120]
capacity = 50
 
max_value, selected_items = knapSack(capacity, weights, values)
# Print all items with selected items in green
print("All items:")
for i in range(len(weights)):
    item_str = f"Item {i}: Value = {values[i]}, Weight = {weights[i]}"
    if i in selected_items:
        print_green(item_str)
    else:
        print(item_str)
print("----------------------")
print("Maximum value:", max_value)
print("Total weight:", sum(weights[i] for i in selected_items), "=<", capacity)