🎒 Knapsack Problem – Complete In-Depth Explanation
The **Knapsack Problem** is a fundamental **optimization problem** in **computer science** and **mathematics**. It involves selecting items to place in a **knapsack (bag)** to maximize the **total value**, while ensuring that the total weight does not exceed a given limit.
📌 Types of Knapsack Problems
The Knapsack problem can be categorized into **three main types**:
- 📦 **0/1 Knapsack Problem** – Items cannot be divided; you either take the entire item or leave it.
- 📉 **Fractional Knapsack Problem** – Items can be divided; you can take fractions of an item.
- 🔄 **Unbounded Knapsack Problem** – An unlimited supply of each item is available.
📌 Understanding the 0/1 Knapsack Problem
Given a set of **N items**, each with a **weight** and **value**, and a knapsack that can carry a maximum **weight W**, determine the **maximum value** that can be obtained by selecting a subset of the items.
🔹 **Mathematical Formulation**
Let:
- 📦 **N** = Total number of items.
- ⚖️ **W** = Maximum weight the knapsack can hold.
- 📊 **w[i]** = Weight of the i-th item.
- 💰 **v[i]** = Value of the i-th item.
- 📝 **x[i]** = 1 if item is included, 0 otherwise.
We need to maximize:
Maximize: Σ (x[i] * v[i]) for i = 1 to N
Subject to: Σ (x[i] * w[i]) ≤ W for i = 1 to N
🛠 Solution Methods for 0/1 Knapsack
📌 **1. Recursion (Brute Force) – O(2^N) Complexity**
A **recursive solution** that checks both possibilities: **including** or **excluding** an item.
int knapsack(int W, vector& weight, vector& value, int n) {
if (n == 0 || W == 0) return 0;
if (weight[n - 1] > W)
return knapsack(W, weight, value, n - 1);
return max(value[n - 1] + knapsack(W - weight[n - 1], weight, value, n - 1),
knapsack(W, weight, value, n - 1));
}
🔴 **Disadvantage:** Exponential complexity **O(2^N)** is inefficient for large N.
📌 **2. Dynamic Programming (O(N × W) Complexity)**
Instead of **recomputing solutions**, we **store** results in a **table (memoization)** and use them to avoid redundant calculations.
🔹 **Bottom-Up Approach (Tabulation Method)**
int knapsackDP(int W, vector& weight, vector& value, int N) {
vector> dp(N + 1, vector(W + 1, 0));
for (int i = 1; i <= N; i++) {
for (int w = 0; w <= W; w++) {
if (weight[i - 1] <= w) {
dp[i][w] = max(value[i - 1] + dp[i - 1][w - weight[i - 1]], dp[i - 1][w]);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[N][W];
}
📌 **3. Space Optimized DP (O(W) Complexity)**
Instead of a **2D table**, we use a **1D array**, updating it from **right to left**.
int knapsackOptimized(int W, vector& weight, vector& value, int N) {
vector dp(W + 1, 0);
for (int i = 0; i < N; i++) {
for (int w = W; w >= weight[i]; w--) {
dp[w] = max(dp[w], value[i] + dp[w - weight[i]]);
}
}
return dp[W];
}
📌 Fractional Knapsack (O(N log N))
Unlike **0/1 Knapsack**, here we can take **fractions** of items.
🔹 **Greedy Strategy:**
- ✅ Sort items by **value/weight ratio**.
- 🔄 Take **whole items** first.
- 📉 If the knapsack is full, take **a fraction** of the next item.
struct Item {
int weight, value;
};
bool cmp(Item a, Item b) {
return (double)a.value / a.weight > (double)b.value / b.weight;
}
double fractionalKnapsack(int W, vector- & items) {
sort(items.begin(), items.end(), cmp);
double maxValue = 0.0;
for (auto item : items) {
if (W >= item.weight) {
maxValue += item.value;
W -= item.weight;
} else {
maxValue += item.value * ((double)W / item.weight);
break;
}
}
return maxValue;
}
⏳ Time Complexity Analysis
Algorithm | Time Complexity |
---|---|
Recursive (Brute Force) | O(2^N) |
Dynamic Programming (2D DP) | O(N × W) |
Space Optimized DP (1D DP) | O(N × W) |
Fractional Knapsack (Greedy) | O(N log N) |
🌍 Real-World Applications of Knapsack Problem
- 📡 **Resource Allocation** – Distributing resources efficiently.
- 🎮 **Game Inventory Management** – Selecting the best combination of items.
- 🛒 **Budget Optimization** – Choosing the best products within a budget.
- 📈 **Stock Market Analysis** – Portfolio selection for maximum returns.