Fractional Knapsack Problem
Problem Statement
Given weights and values of n items, and a knapsack with capacity W, find the maximum value that can be put in the knapsack. Unlike 0/1 Knapsack, you can break items (take fractions of items).
Constraints:
- 1 ≤ n ≤ 10^5
- 1 ≤ W ≤ 10^9
- 1 ≤ weight[i], value[i] ≤ 10^6
Example:
- Input:
weights = [10, 20, 30],values = [60, 100, 120],W = 50 - Output:
240 - Explanation: Take full items of weight 10, 20 (value = 60 + 100 = 160), then 2/3 of item with weight 30 (value = 80). Total = 240.
Example 2:
- Input:
weights = [10, 40, 20, 30],values = [60, 40, 100, 120],W = 50 - Output:
240 - Explanation: Take items with best value/weight ratio first.
Approach 1: Brute Force (Try All Fractions)
Intuition
Try all possible combinations of items and fractions. This is impractical but shows the exponential nature of the problem without the greedy insight.
Algorithm
- Generate all possible selections
- For each item, try taking various fractions (0, 0.1, 0.2, ..., 1)
- Track maximum value while staying within capacity
import java.util.*;
public class Solution {
private double maxValue = 0;
private void solve(int[][] items, int idx, double capacity, double value) {
maxValue = Math.max(maxValue, value);
if (idx == items.length || capacity <= 0) return;
double maxFraction = Math.min(1.0, capacity / items[idx][0]);
for (double frac = maxFraction; frac >= 0; frac -= 0.1) {
double weight = frac * items[idx][0];
double val = frac * items[idx][1];
solve(items, idx + 1, capacity - weight, value + val);
}
}
public double fractionalKnapsack(int[] weights, int[] values, int W) {
int n = weights.length;
int[][] items = new int[n][2];
for (int i = 0; i < n; i++) {
items[i][0] = weights[i];
items[i][1] = values[i];
}
maxValue = 0;
solve(items, 0, W, 0);
return maxValue;
}
}Complexity Analysis
- Time Complexity: O(2^n) - Exponential due to trying all combinations
- Space Complexity: O(n) - Recursion stack
Approach 2: Greedy (Sort by Value/Weight Ratio) - Optimal
Intuition
The greedy approach is to always pick the item with the highest value per unit weight. Since we can take fractions, we should maximize value density.
Key Insight: Items with higher value/weight ratio give more value per unit capacity used.
Algorithm
- Calculate value/weight ratio for each item
- Sort items by ratio in descending order
- Take items greedily starting from highest ratio
- If item fits completely, take it entirely
- Otherwise, take the fraction that fits
import java.util.*;
class Item implements Comparable<Item> {
int weight;
int value;
double ratio;
int index;
Item(int weight, int value, int index) {
this.weight = weight;
this.value = value;
this.ratio = (double) value / weight;
this.index = index;
}
@Override
public int compareTo(Item other) {
return Double.compare(other.ratio, this.ratio); // Descending
}
}
public class FractionalKnapsack {
public double solve(int[] weights, int[] values, int W) {
int n = weights.length;
// Create items with ratio
Item[] items = new Item[n];
for (int i = 0; i < n; i++) {
items[i] = new Item(weights[i], values[i], i);
}
// Sort by value/weight ratio in descending order
Arrays.sort(items);
double totalValue = 0;
double remainingCapacity = W;
for (Item item : items) {
if (remainingCapacity <= 0) break;
if (item.weight <= remainingCapacity) {
// Take entire item
totalValue += item.value;
remainingCapacity -= item.weight;
} else {
// Take fraction
double fraction = remainingCapacity / item.weight;
totalValue += fraction * item.value;
remainingCapacity = 0;
}
}
return totalValue;
}
public double[] getDetailedSolution(int[] weights, int[] values, int W) {
int n = weights.length;
Item[] items = new Item[n];
for (int i = 0; i < n; i++) {
items[i] = new Item(weights[i], values[i], i);
}
Arrays.sort(items);
double[] fractions = new double[n];
double remainingCapacity = W;
for (Item item : items) {
if (remainingCapacity <= 0) break;
if (item.weight <= remainingCapacity) {
fractions[item.index] = 1.0;
remainingCapacity -= item.weight;
} else {
fractions[item.index] = remainingCapacity / item.weight;
remainingCapacity = 0;
}
}
return fractions;
}
public static void main(String[] args) {
FractionalKnapsack fk = new FractionalKnapsack();
int[] weights = {10, 20, 30};
int[] values = {60, 100, 120};
int W = 50;
System.out.println("Maximum value: " + fk.solve(weights, values, W)); // 240
}
}Dry Run Example
weights = [10, 20, 30]
values = [60, 100, 120]
W = 50
Calculate ratios:
Item 0: value/weight = 60/10 = 6.0
Item 1: value/weight = 100/20 = 5.0
Item 2: value/weight = 120/30 = 4.0
After sorting by ratio (descending):
Item 0 (ratio=6.0), Item 1 (ratio=5.0), Item 2 (ratio=4.0)
Greedy selection:
remaining_capacity = 50, total_value = 0
Step 1: Item 0 (weight=10, value=60, ratio=6.0)
- 10 <= 50, take entire item
- remaining_capacity = 50 - 10 = 40
- total_value = 0 + 60 = 60
Step 2: Item 1 (weight=20, value=100, ratio=5.0)
- 20 <= 40, take entire item
- remaining_capacity = 40 - 20 = 20
- total_value = 60 + 100 = 160
Step 3: Item 2 (weight=30, value=120, ratio=4.0)
- 30 > 20, take fraction
- fraction = 20/30 = 2/3
- value_taken = (2/3) × 120 = 80
- remaining_capacity = 0
- total_value = 160 + 80 = 240
Result: Maximum value = 240
Items taken: Item 0 (100%), Item 1 (100%), Item 2 (66.67%)
Complexity Analysis
- Time Complexity: O(n log n)
- O(n) for calculating ratios
- O(n log n) for sorting
- O(n) for greedy selection
- Space Complexity: O(n) for storing items (can be O(1) with in-place modifications)
Comparison: Fractional vs 0/1 Knapsack
| Aspect | Fractional Knapsack | 0/1 Knapsack | |--------|---------------------|--------------| | Items | Can take fractions | Must take whole or nothing | | Approach | Greedy | Dynamic Programming | | Complexity | O(n log n) | O(nW) | | Optimality | Greedy is optimal | Greedy is NOT optimal |
Key Takeaways
- Greedy by Ratio: Sort by value/weight ratio in descending order
- Fractions Allowed: The key that makes greedy work
- Take Greedily: Always take item with highest ratio first
- Partial Taking: If item doesn't fit, take the fraction that fits
- Optimal Proof: Exchange argument shows greedy is optimal
- vs 0/1 Knapsack: 0/1 requires DP, fractional uses greedy
- Real Applications: Resource allocation, cargo loading
- Edge Cases: Single item, all items fit, no item fits