Chariot Arcana: Activity Selection & Fractional Knapsack
Tags: #◇CSALGCM #◇Problem Sets #◇HackerRank #◇Algorithms #◇Greedy Algorithms
◇Overview
The Chariot Arcana emphasizes optimal forward progress without backtracking: The Greedy Paradigm. Solutions make the locally optimal choice at each step to arrive at a globally optimal solution. This problem set explores Activity Selection (Interval Scheduling) and the Fractional Knapsack Problem.
◇Problem 1: Activity Selection (Interval Scheduling)
Problem Description
Given a set of activities , where each activity has a start time and a finish time (). Two activities and are compatible if their time intervals do not overlap ( or ). Find a maximum-cardinality subset of mutually compatible activities.
Constraints:
Greedy Choice Property: The Exchange Argument Proof
Greedy Strategy: Sort all activities in non-decreasing order of their finish times: . Always pick the activity with the earliest finish time that is compatible with previously selected activities.
Stepped Proof by Exchange Argument:
- Let be the activity with the earliest finish time in the current remaining candidate set.
- Suppose an optimal solution does not contain . Let the first activity in ordered by finish time be ().
- Since has the minimal finish time, .
- Replacing with in creates a new subset .
- Because , finishes before or at the same time did; hence, cannot conflict with any subsequent activities in .
- Therefore, is also a valid compatible subset with , proving that the greedy choice belongs to at least one optimal solution.
Stepped Trace
Consider activities sorted by finish time :
| Activity | Start | Finish | Action | Reason |
|---|---|---|---|---|
| 1 | 2 | Select | First activity with earliest finish () | |
| 3 | 4 | Select | () | |
| 0 | 6 | Reject | (Overlaps) | |
| 5 | 7 | Select | () | |
| 8 | 9 | Select | () | |
| 5 | 9 | Reject | (Overlaps) |
Maximum compatible activities selected: 4 ().
◇Problem 2: Fractional Knapsack Problem
Problem Description
Given a knapsack with maximum weight capacity , and items where each item has weight and value . Unlike the 0/1 knapsack, fractions of items may be taken (e.g., of item , contributing weight and value). Maximize total value.
Constraints:
Approach & Intuition
Compute the value density (value per unit weight) for each item:
Sort items in descending order of density: .
Greedily consume items in this order: take the full item if remaining capacity permits; otherwise, take whatever fraction fills the remaining capacity and terminate.
Stepped Calculation Trace
Let , with 3 items:
- Item 1:
- Item 2:
- Item 3:
- Item 1: . Take 100%: . Added value = 60. Remaining .
- Item 2: . Take 100%: . Added value = 100. Remaining .
- Item 3: . Take fraction . Added value = . Remaining .
- Total Maximum Value: .
◇Complete C++ Implementation
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
struct Activity {
int start;
int finish;
};
struct Item {
double value;
double weight;
};
// Problem 1: Activity Selection
int maxActivities(vector<Activity>& acts) {
// Sort strictly by finish time
sort(acts.begin(), acts.end(), [](const Activity& a, const Activity& b) {
return a.finish < b.finish;
});
int count = 0;
int lastFinish = -1;
for (const auto& act : acts) {
if (act.start >= lastFinish) {
count++;
lastFinish = act.finish;
}
}
return count;
}
// Problem 2: Fractional Knapsack
double fractionalKnapsack(double W, vector<Item>& items) {
// Sort descending by value-to-weight ratio
sort(items.begin(), items.end(), [](const Item& a, const Item& b) {
return (a.value / a.weight) > (b.value / b.weight);
});
double totalValue = 0.0;
double currentWeight = 0.0;
for (const auto& item : items) {
if (currentWeight + item.weight <= W) {
currentWeight += item.weight;
totalValue += item.value;
} else {
double remain = W - currentWeight;
totalValue += item.value * (remain / item.weight);
break; // Knapsack is full
}
}
return totalValue;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
// Test Activity Selection
vector<Activity> activities = {{1, 2}, {3, 4}, {0, 6}, {5, 7}, {8, 9}, {5, 9}};
cout << "Max Compatible Activities: " << maxActivities(activities) << "\n"; // Output: 4
// Test Fractional Knapsack
vector<Item> items = {{60, 10}, {100, 20}, {120, 30}};
double W = 50;
cout << fixed << setprecision(2);
cout << "Max Knapsack Value: " << fractionalKnapsack(W, items) << "\n"; // Output: 240.00
return 0;
}
◇References & Navigation
- Previous Topic: ◇[PS] Lovers Arcana
- Next Topic: ◇[PS] Hermit Arcana
- Related Notes: ◇Greedy Algorithms Introduction