-
Notifications
You must be signed in to change notification settings - Fork 278
Expand file tree
/
Copy pathknapsack_2d_dp.py
More file actions
154 lines (122 loc) · 4.99 KB
/
knapsack_2d_dp.py
File metadata and controls
154 lines (122 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""
0/1 Knapsack Problem using 2D Dynamic Programming
-------------------------------------------------
Objective:
This file explains the **2D DP approach** to the classic 0/1 Knapsack problem
in the simplest and most educational way possible.
Problem Statement:
Given N items, each with a certain weight and value, and a bag with a maximum weight
capacity (W), we must select items to maximize total value without exceeding W.
This is a perfect problem to understand **2D Dynamic Programming**.
-------------------------------------------------
Approach Explanation
-------------------------------------------------
We build a DP table (2D array) where:
dp[i][w] = Maximum value we can achieve
using the first i items (0..i-1) and
capacity w.
Transition:
If we do NOT take the ith item → dp[i][w] = dp[i-1][w]
If we take the ith item (if it fits) → dp[i][w] = value[i-1] + dp[i-1][w - weight[i-1]]
Final Answer → dp[n][W]
-------------------------------------------------
Complexity Analysis:
-------------------------------------------------
Time Complexity: O(N * W)
Space Complexity: O(N * W)
Where:
N = number of items
W = maximum capacity of the bag
"""
# -------------------------------------------------
# Step 1: Define the knapsack solver function
# -------------------------------------------------
def knapsack_2d(weights, values, capacity):
"""
Solves the 0/1 Knapsack problem using 2D Dynamic Programming.
Parameters:
weights : list of item weights
values : list of item values
capacity : maximum capacity of the bag
Returns:
max_value : Maximum value that can be obtained
chosen_items : List of item indices chosen
dp : 2D DP table
"""
n = len(weights)
# Create DP table with all 0s
dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]
# -------------------------------------------------
# Step 2: Fill DP table
# -------------------------------------------------
for i in range(1, n + 1):
for w in range(1, capacity + 1):
# If current item's weight is more than current capacity, skip it
if weights[i - 1] > w:
dp[i][w] = dp[i - 1][w]
else:
# Either we take the item or we don’t — choose max
include = values[i - 1] + dp[i - 1][w - weights[i - 1]]
exclude = dp[i - 1][w]
dp[i][w] = max(include, exclude)
# -------------------------------------------------
# Step 3: Backtrack to find chosen items
# -------------------------------------------------
chosen_items = []
w = capacity
for i in range(n, 0, -1):
# If value comes from including the current item
if dp[i][w] != dp[i - 1][w]:
chosen_items.append(i - 1)
w -= weights[i - 1]
chosen_items.reverse() # maintain original order
# -------------------------------------------------
# Step 4: Return final results
# -------------------------------------------------
max_value = dp[n][capacity]
return max_value, chosen_items, dp
# -------------------------------------------------
# Step 5: Utility function to print DP table
# -------------------------------------------------
def print_dp_table(dp, weights, values):
"""
Prints the DP table in a readable format with item info.
"""
n = len(weights)
cap = len(dp[0]) - 1
print("\n2D DP Table (rows = items, columns = capacity):\n")
header = ["w\\i"] + [str(i) for i in range(cap + 1)]
print("\t".join(header))
for i in range(n + 1):
row_label = "0 (no items)" if i == 0 else f"{i} (wt={weights[i-1]}, val={values[i-1]})"
row_values = [str(dp[i][j]) for j in range(cap + 1)]
print(row_label + "\t" + "\t".join(row_values))
# -------------------------------------------------
# Step 6: Run example test cases
# -------------------------------------------------
if __name__ == "__main__":
# Example 1 (Basic)
weights = [2, 1, 3, 2]
values = [12, 10, 20, 15]
capacity = 5
print("Example 1: Understanding 2D DP through 0/1 Knapsack\n")
print(f"Weights : {weights}")
print(f"Values : {values}")
print(f"Capacity: {capacity}\n")
max_value, chosen, dp = knapsack_2d(weights, values, capacity)
# Print DP table
print_dp_table(dp, weights, values)
print("\nFinal Results:")
print(f"Maximum Value: {max_value}")
print(f"Chosen Item Indices: {chosen}")
print("Chosen Items (index → weight, value):")
for idx in chosen:
print(f" Item {idx}: (w={weights[idx]}, v={values[idx]})")
# Example 2 (Edge Case: small capacity)
print("\nExample 2: Edge Case with Small Capacity")
weights2 = [4, 5, 1]
values2 = [1, 2, 3]
cap2 = 4
max_val2, chosen2, dp2 = knapsack_2d(weights2, values2, cap2)
print(f"Weights: {weights2}, Values: {values2}, Capacity: {cap2}")
print(f"Max Value = {max_val2}, Chosen = {chosen2}")