The Activity Selection Problem is a classic optimization problem that involves selecting the maximum number of activities that can be performed by a single person or machine, given a set of activities each with a start and finish time. The challenge lies in ensuring that no two selected activities overlap.
This tutorial will guide you through understanding the problem, its solution using greedy algorithms, and implementing it in code. By the end, you'll have a solid grasp of how to solve this problem efficiently.
You are given n activities, each with a start time (s[i]) and a finish time (f[i]). The goal is to select the maximum number of non-overlapping activities that can be performed. An activity i overlaps with another activity j if they share any common time.
The greedy approach for solving the Activity Selection Problem involves selecting activities in such a way that we always pick the next activity that finishes as early as possible and does not overlap with the previously selected activity. This ensures that we leave maximum room for other activities.
The greedy choice property ensures that by always picking the next activity that finishes as early as possible, we maximize the number of activities that can be performed. This approach works because once an activity is chosen, all subsequent activities must start after its finish time, leaving more room for other activities to be selected.
Let's implement the Activity Selection Problem in Python using a greedy algorithm. We'll define a function activity_selection that takes two lists: one for start times and another for finish times.
def activity_selection(start_times, finish_times):
# Combine start and finish times into a list of tuples (finish_time, start_time)
activities = [(f, s) for f, s in zip(finish_times, start_times)]
# Sort activities by their finish time; if same, sort by start time
activities.sort()
# Initialize the list to store selected activities
selected_activities = []
# Select the first activity (earliest finish time)
last_selected_finish_time = activities[0][0]
selected_activities.append(activities[0])
# Iterate over remaining activities
for f, s in activities[1:]:
if s >= last_selected_finish_time:
# If current activity starts after or when the last selected activity finishes,
# select it and update the last selected finish time
selected_activities.append((f, s))
last_selected_finish_time = f
return selected_activities
# Example usage
start_times = [1, 3, 0, 5, 8, 5]
finish_times = [2, 4, 6, 7, 9, 9]
selected = activity_selection(start_times, finish_times)
print("Selected activities (finish_time, start_time):", selected)
\(O(n \log n)\) due to sorting, followed by a linear scan \(O(n)\), making it efficient for large datasets.The Activity Selection Problem has numerous real-world applications, including:
The Activity Selection Problem is an excellent example of how greedy algorithms can be used to solve optimization problems efficiently. By always making the locally optimal choice, we ensure a globally optimal solution. Understanding this problem and its implementation will enhance your ability to tackle similar scheduling and resource allocation challenges in various domains.