diff --git a/dynamic_programming/knapsack.py b/dynamic_programming/knapsack.py index 28c5b19dbe36..9d5155cdd7e8 100644 --- a/dynamic_programming/knapsack.py +++ b/dynamic_programming/knapsack.py @@ -39,6 +39,72 @@ def knapsack(w, wt, val, n): return dp[n][w_], dp +def knapsack_space_optimized( + capacity: int, weights: list[int], values: list[int], num_items: int +) -> int: + """ + Solve the 0/1 knapsack problem with O(capacity) extra space. + + It uses a 1D dynamic programming array and iterates capacities in reverse + for each item to avoid reusing the same item more than once. + + >>> knapsack_space_optimized(50, [10, 20, 30], [60, 100, 120], 3) + 220 + >>> knapsack_space_optimized(0, [10, 20, 30], [60, 100, 120], 3) + 0 + >>> knapsack_space_optimized(6, [4, 3, 2, 3], [3, 2, 4, 4], 4) + 8 + >>> knapsack_space_optimized(-1, [1], [1], 1) + Traceback (most recent call last): + ... + ValueError: The knapsack capacity cannot be negative. + >>> knapsack_space_optimized(1, [1], [1], -1) + Traceback (most recent call last): + ... + ValueError: The number of items cannot be negative. + >>> knapsack_space_optimized(1, [1], [1], 2) + Traceback (most recent call last): + ... + ValueError: The number of items exceeds the provided input lengths. + >>> knapsack_space_optimized(1, [-1], [1], 1) + Traceback (most recent call last): + ... + ValueError: Weight at index 0 cannot be negative. + >>> knapsack_space_optimized(1, [1], [1.5], 1) + Traceback (most recent call last): + ... + TypeError: Value at index 0 must be an integer. + """ + if num_items < 0: + raise ValueError("The number of items cannot be negative.") + if capacity < 0: + raise ValueError("The knapsack capacity cannot be negative.") + if num_items > len(weights) or num_items > len(values): + raise ValueError("The number of items exceeds the provided input lengths.") + for item_index in range(num_items): + item_weight = weights[item_index] + item_value = values[item_index] + if not isinstance(item_weight, int): + msg = f"Weight at index {item_index} must be an integer." + raise TypeError(msg) + if item_weight < 0: + msg = f"Weight at index {item_index} cannot be negative." + raise ValueError(msg) + if not isinstance(item_value, int): + msg = f"Value at index {item_index} must be an integer." + raise TypeError(msg) + + dp = [0] * (capacity + 1) + for item_index in range(num_items): + item_weight = weights[item_index] + item_value = values[item_index] + for current_capacity in range(capacity, item_weight - 1, -1): + dp[current_capacity] = max( + dp[current_capacity], item_value + dp[current_capacity - item_weight] + ) + return dp[capacity] + + def knapsack_with_example_solution(w: int, wt: list, val: list): """ Solves the integer weights knapsack problem returns one of