Bird
Raised Fist0
DSA Pythonprogramming~15 mins

Activity Selection Problem in DSA Python - Deep Dive

Choose your learning style10 modes available
Overview - Activity Selection Problem
What is it?
The Activity Selection Problem is about choosing the maximum number of activities that don't overlap in time. Each activity has a start time and an end time. The goal is to select activities so that no two chosen activities happen at the same time. This helps in scheduling tasks efficiently.
Why it matters
Without this problem's solution, we might waste time or resources by scheduling overlapping activities. For example, if you book two meetings at the same time, you can't attend both. Solving this problem helps in planning events, managing resources, and optimizing time usage in real life and computer systems.
Where it fits
Before learning this, you should understand arrays or lists and sorting basics. After this, you can explore more complex scheduling problems like interval partitioning or dynamic programming approaches for weighted activities.
Mental Model
Core Idea
Pick the earliest finishing activity first to leave room for more activities later.
Think of it like...
Imagine you have a single meeting room and many people want to use it. To fit the most meetings, you always let the meeting that ends soonest happen first, so the room becomes free quickly for others.
Activities sorted by finish time:

Start: 1  3  0  5  8  5
Finish:2  4  6  7  9  9

Select activities greedily:
[1-2] -> [3-4] -> [5-7] -> [8-9]

Result: 4 activities selected without overlap.
Build-Up - 7 Steps
1
FoundationUnderstanding Activities and Time Slots
πŸ€”
Concept: Introduce what activities are and how start and end times define them.
An activity is a task with a start time and an end time. For example, Activity A starts at 1 and ends at 2. If two activities overlap in time, you cannot do both. We want to find a way to pick activities that don't overlap.
Result
You understand that activities are intervals on a timeline and overlapping means their times clash.
Understanding activities as time intervals is key to seeing why some can't happen together.
2
FoundationSorting Activities by Finish Time
πŸ€”
Concept: Sorting activities by their end times helps in making the best choices.
Given a list of activities, we sort them by their finish times from earliest to latest. This order helps us pick activities that free up time quickly for others.
Result
Activities are arranged so that the one finishing earliest comes first.
Sorting by finish time sets the stage for a simple greedy choice that leads to the optimal solution.
3
IntermediateGreedy Selection of Activities
πŸ€”Before reading on: Do you think picking the longest activity first or the earliest finishing activity first leads to more total activities? Commit to your answer.
Concept: Choosing the earliest finishing activity first maximizes the number of activities you can attend.
Start with the first activity in the sorted list (earliest finish). Then, pick the next activity that starts after the current one ends. Repeat until no more activities fit.
Result
You get the maximum number of non-overlapping activities selected.
Knowing that earliest finishing time leads to optimal selection avoids complex checks and ensures maximum usage.
4
IntermediateImplementing the Algorithm in Python
πŸ€”
Concept: Translate the greedy approach into runnable Python code.
activities = [(1,2), (3,4), (0,6), (5,7), (8,9), (5,9)] # Sort by finish time activities.sort(key=lambda x: x[1]) selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish print(selected)
Result
[(1, 2), (3, 4), (5, 7), (8, 9)]
Seeing the code output confirms the greedy method works and helps connect theory to practice.
5
IntermediateDry Run with Example Activities
πŸ€”Before reading on: Predict which activities will be selected from [(1,3),(2,5),(4,7),(6,9)]. Commit to your answer.
Concept: Step through the algorithm to understand how selections happen.
Sorted by finish time: (1,3), (2,5), (4,7), (6,9) Pick (1,3) first. Next, (2,5) starts before 3 ends, skip. Next, (4,7) starts after 3, pick. Next, (6,9) starts after 7 ends, pick. Selected: (1,3), (4,7), (6,9)
Result
Selected activities: [(1,3), (4,7), (6,9)]
Dry runs build intuition on how the algorithm avoids overlaps and why some activities are skipped.
6
AdvancedHandling Edge Cases and Equal Finish Times
πŸ€”Before reading on: If two activities finish at the same time, does picking either first affect the total count? Commit to your answer.
Concept: Understand how ties in finish times affect selection and how to handle them.
When activities have the same finish time, picking any one of them first is fine as long as it doesn't overlap with previously selected activities. Sorting is stable, so the order remains consistent. This ensures the greedy approach still works.
Result
Algorithm correctly selects maximum activities even with equal finish times.
Knowing that stable sorting and tie handling preserve correctness prevents subtle bugs in implementation.
7
ExpertWhy Greedy Always Works Here
πŸ€”Before reading on: Does the greedy choice property hold for all scheduling problems? Commit to yes or no.
Concept: Explore the proof idea why picking earliest finishing activity leads to optimal solution.
The problem has the greedy choice property: choosing the earliest finishing activity leaves the most room for others. Also, it has optimal substructure: the problem reduces to selecting from remaining activities after the chosen one. These properties guarantee greedy correctness.
Result
You understand the theoretical foundation behind the algorithm's optimality.
Understanding these properties helps recognize when greedy algorithms apply and when they don't.
Under the Hood
The algorithm sorts activities by their finish times, then iterates through them, selecting each activity that starts after the last selected activity ends. This works because selecting the earliest finishing activity frees up time for more activities. Internally, sorting takes O(n log n) time, and selection is O(n), making the overall complexity O(n log n).
Why designed this way?
The problem was designed to maximize non-overlapping intervals. Early researchers found that greedy choice of earliest finish time leads to optimal solutions, avoiding complex dynamic programming. This simplicity and efficiency made it a classic example in algorithm design.
Activities sorted by finish time:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Activity 1  β”‚ Finish: 2
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Activity 2  β”‚ Finish: 4
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Activity 3  β”‚ Finish: 6
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Activity 4  β”‚ Finish: 7
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Activity 5  β”‚ Finish: 9
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Selection flow:

Start -> Pick Activity 1 (ends earliest)
       -> Skip overlapping activities
       -> Pick next compatible activity
       -> Repeat until done
Myth Busters - 3 Common Misconceptions
Quick: Does picking the longest activity first always maximize total activities? Commit yes or no.
Common Belief:Choosing the longest activity first will maximize the total number of activities.
Tap to reveal reality
Reality:Picking the longest activity first often reduces the number of activities you can schedule because it blocks more time.
Why it matters:If you pick longest activities first, you may end up with fewer total activities, wasting opportunities to schedule more.
Quick: Can sorting activities by start time alone solve the problem optimally? Commit yes or no.
Common Belief:Sorting activities by their start times and picking earliest starting ones leads to the best schedule.
Tap to reveal reality
Reality:Sorting by start time does not guarantee maximum activities; earliest finishing time sorting is necessary.
Why it matters:Sorting by start time can cause overlaps and fewer selected activities, leading to suboptimal schedules.
Quick: Is the greedy approach always optimal for all scheduling problems? Commit yes or no.
Common Belief:Greedy algorithms always find the best schedule for any activity selection problem.
Tap to reveal reality
Reality:Greedy works only for unweighted activity selection; weighted or more complex variants need dynamic programming.
Why it matters:Using greedy blindly on weighted problems leads to wrong answers and inefficient schedules.
Expert Zone
1
The greedy choice property depends on activities being unweighted; adding weights breaks this property.
2
Stable sorting ensures consistent selection order when finish times tie, preventing subtle bugs.
3
The problem's optimal substructure allows recursive or iterative greedy solutions, but dynamic programming is needed for weighted variants.
When NOT to use
Do not use this greedy approach when activities have weights or profits; instead, use dynamic programming for weighted interval scheduling. Also, if activities can be preempted or partially scheduled, other algorithms apply.
Production Patterns
Used in CPU job scheduling where tasks are non-preemptive and have fixed start/end times. Also applied in event planning, resource allocation, and booking systems to maximize utilization without conflicts.
Connections
Interval Partitioning
Builds-on
Understanding activity selection helps grasp interval partitioning, which assigns activities to multiple resources minimizing resource count.
Weighted Interval Scheduling
Contrasts with
Knowing greedy fails for weighted cases highlights when to use dynamic programming instead.
Project Management
Application domain
Activity selection principles apply to scheduling tasks in projects to avoid conflicts and optimize timelines.
Common Pitfalls
#1Selecting activities based on earliest start time instead of earliest finish time.
Wrong approach:activities.sort(key=lambda x: x[0]) selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish print(selected)
Correct approach:activities.sort(key=lambda x: x[1]) selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish print(selected)
Root cause:Misunderstanding that earliest start time does not guarantee maximum non-overlapping activities.
#2Not sorting activities before selection.
Wrong approach:selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish print(selected)
Correct approach:activities.sort(key=lambda x: x[1]) selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish print(selected)
Root cause:Skipping sorting breaks the greedy logic, leading to suboptimal or incorrect selections.
#3Applying greedy approach to weighted activities without modification.
Wrong approach:activities = [(1,3,5), (2,5,6), (4,7,5)] # (start, finish, weight) activities.sort(key=lambda x: x[1]) selected = [] last_finish = 0 for start, finish, weight in activities: if start >= last_finish: selected.append((start, finish, weight)) last_finish = finish print(selected)
Correct approach:# Use dynamic programming for weighted interval scheduling # Not shown here due to complexity pass
Root cause:Greedy ignores weights, so it cannot maximize total weight, only count.
Key Takeaways
The Activity Selection Problem finds the maximum number of non-overlapping activities by choosing the earliest finishing activity first.
Sorting activities by their finish times is essential to apply the greedy selection correctly.
Greedy algorithms work here because the problem has the greedy choice property and optimal substructure.
This approach is efficient with O(n log n) time due to sorting and simple iteration.
For weighted or more complex scheduling, greedy fails and dynamic programming is required.

Practice

(1/5)
1. What is the main goal of the Activity Selection Problem?
easy
A. Select the maximum number of activities that don't overlap in time
B. Find the activity with the longest duration
C. Sort activities by their start times
D. Calculate the total duration of all activities

Solution

  1. Step 1: Understand the problem goal

    The Activity Selection Problem aims to choose the largest set of activities that do not overlap in their time intervals.
  2. Step 2: Identify the correct goal

    Among the options, only selecting the maximum number of non-overlapping activities matches the problem's goal.
  3. Final Answer:

    Select the maximum number of activities that don't overlap in time -> Option A
  4. Quick Check:

    Goal = Max non-overlapping activities [OK]
Hint: Focus on maximizing non-overlapping activities [OK]
Common Mistakes:
  • Confusing longest duration with maximum count
  • Sorting by start time only
  • Summing durations instead of counting activities
2. Which of the following is the correct Python syntax to sort a list of activities by their finish times stored as tuples (start, finish)?
easy
A. activities.sort(key=lambda x: x[0])
B. activities.sort(key=lambda x: x[1])
C. activities.sort(reverse=True)
D. activities.sort(key=lambda x: x[0], reverse=True)

Solution

  1. Step 1: Identify sorting key for finish times

    Finish time is the second element in the tuple, so index 1 is used as the key.
  2. Step 2: Choose correct sort syntax

    Using activities.sort(key=lambda x: x[1]) sorts by finish time ascending, which is required.
  3. Final Answer:

    activities.sort(key=lambda x: x[1]) -> Option B
  4. Quick Check:

    Sort by finish time = key index 1 [OK]
Hint: Sort by finish time using key=lambda x: x[1] [OK]
Common Mistakes:
  • Sorting by start time instead of finish time
  • Using reverse sort without reason
  • Incorrect lambda index for key
3. What is the output of the following code?
activities = [(1, 4), (3, 5), (0, 6), (5, 7), (8, 9), (5, 9)]
activities.sort(key=lambda x: x[1])
selected = []
last_finish = 0
for start, finish in activities:
    if start >= last_finish:
        selected.append((start, finish))
        last_finish = finish
print(selected)
medium
A. [(1, 4), (3, 5), (5, 7), (8, 9)]
B. [(0, 6), (8, 9)]
C. [(1, 4), (5, 7), (8, 9)]
D. [(1, 4), (5, 9)]

Solution

  1. Step 1: Sort activities by finish time

    Sorted list: [(1,4), (3,5), (0,6), (5,7), (8,9), (5,9)]
  2. Step 2: Select activities greedily

    Start with last_finish=0
    - (1,4): start=1 >=0, select, last_finish=4
    - (3,5): start=3 <4, skip
    - (0,6): start=0 <4, skip
    - (5,7): start=5 >=4, select, last_finish=7
    - (8,9): start=8 >=7, select, last_finish=9
    - (5,9): start=5 <9, skip
  3. Final Answer:

    [(1, 4), (5, 7), (8, 9)] -> Option C
  4. Quick Check:

    Greedy selection = [(1,4),(5,7),(8,9)] [OK]
Hint: Pick earliest finishing activities first [OK]
Common Mistakes:
  • Selecting overlapping activities
  • Not updating last_finish correctly
  • Sorting incorrectly
4. Find the error in this code snippet for the Activity Selection Problem:
activities = [(1, 4), (4, 5), (2, 6)]
activities.sort(key=lambda x: x[1])
selected = []
last_finish = 0
for start, finish in activities:
    if start > last_finish:
        selected.append((start, finish))
        last_finish = finish
print(selected)
medium
A. Using 'start > last_finish' instead of 'start >= last_finish'
B. Missing parentheses in print statement
C. Not initializing selected list
D. Sorting by start time instead of finish time

Solution

  1. Step 1: Check sorting key

    Correctly sorts activities by finish time (second element, index 1).
  2. Step 2: Check condition in if statement

    Condition uses 'start > last_finish' which excludes activities starting exactly at last_finish time, missing valid selections. It should be 'start >= last_finish'.
  3. Final Answer:

    Using 'start > last_finish' instead of 'start >= last_finish' -> Option A
  4. Quick Check:

    Condition must allow equal start time [OK]
Hint: Use >= to allow back-to-back activities [OK]
Common Mistakes:
  • Using > excludes valid activities
  • Sorting by start time only
  • Forgetting to update last_finish
5. Given activities with start and finish times:
[(1, 2), (3, 4), (0, 6), (5, 7), (8, 9), (5, 9)]
How many activities can be selected to maximize the count without overlap, and which are they?
hard
A. 2 activities: (0, 6), (8, 9)
B. 3 activities: (0, 6), (5, 7), (8, 9)
C. 5 activities: (1, 2), (3, 4), (5, 7), (8, 9), (5, 9)
D. 4 activities: (1, 2), (3, 4), (5, 7), (8, 9)

Solution

  1. Step 1: Sort activities by finish time

    Sorted list: [(1,2), (3,4), (0,6), (5,7), (8,9), (5,9)]
  2. Step 2: Select activities greedily

    Start last_finish=0
    - (1,2): start=1 >=0, select, last_finish=2
    - (3,4): start=3 >=2, select, last_finish=4
    - (0,6): start=0 <4, skip
    - (5,7): start=5 >=4, select, last_finish=7
    - (8,9): start=8 >=7, select, last_finish=9
    - (5,9): start=5 <9, skip
    Total selected = 4 activities
  3. Final Answer:

    4 activities: (1, 2), (3, 4), (5, 7), (8, 9) -> Option D
  4. Quick Check:

    Max count = 4 selected activities [OK]
Hint: Sort by finish time, pick earliest finishing activities [OK]
Common Mistakes:
  • Including overlapping activities
  • Counting all activities without checking overlap
  • Choosing activities with longer duration instead of earliest finish
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard