Bird
Raised Fist0
DSA C#programming~15 mins

Activity Selection Problem in DSA C# - 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 from a list of activities, each with a start and end time. The goal is to attend as many activities as possible without any two happening at the same time. This problem helps us understand how to make the best choices when resources like time are limited. It is a classic example of a greedy algorithm in action.
Why it matters
Without this concept, we might waste time or resources by choosing overlapping activities, leading to fewer total activities completed. It helps in scheduling tasks efficiently, like planning meetings or events, so we get the most done without conflicts. This problem shows how simple rules can lead to the best overall plan, which is important in many real-life situations.
Where it fits
Before learning this, you should understand arrays or lists and basic sorting. After this, you can explore other greedy algorithms and more complex scheduling problems like interval partitioning or dynamic programming approaches to scheduling.
Mental Model
Core Idea
Pick the earliest finishing activity first to leave room for as many activities as possible afterward.
Think of it like...
Imagine you have a single chair and many friends want to sit for different times. To let the most friends sit, you let the friend who finishes earliest leave first, so the next friend can use the chair sooner.
Activities sorted by finish time:
┌───────────────┐
│ Activity List │
├───────────────┤
│ A1: 1 - 4    │
│ A2: 3 - 5    │
│ A3: 0 - 6    │
│ A4: 5 - 7    │
│ A5: 8 - 9    │
│ A6: 5 - 9    │
└───────────────┘

Selection process:
Start with A1 (finishes at 4)
Skip A2 (starts at 3, overlaps)
Skip A3 (starts at 0, overlaps)
Pick A4 (starts at 5, after 4)
Pick A5 (starts at 8, after 7)
Skip A6 (starts at 5, overlaps with A4)

Result: A1 -> A4 -> A5
Build-Up - 7 Steps
1
FoundationUnderstanding Activity Scheduling Basics
🤔
Concept: Introduce what activities are and how start and end times define them.
Activities are tasks with a start time and an end time. For example, an activity might start at 1 PM and end at 4 PM. We want to pick activities that do not overlap, meaning one must finish before the next starts.
Result
You understand that overlapping activities cannot be done together.
Knowing what overlapping means is essential to solving scheduling problems.
2
FoundationSorting Activities by Finish Time
🤔
Concept: Sorting activities by their finishing times helps us decide which to pick first.
Given a list of activities, we sort them so the one that finishes earliest comes first. For example, activities ending at 4, 5, 6, 7, 9 will be ordered from earliest to latest finish time.
Result
Activities are ordered to prioritize those that free up time sooner.
Sorting by finish time sets the stage for a simple, effective selection strategy.
3
IntermediateGreedy Choice: Pick Earliest Finishing Activity
🤔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 activity that finishes earliest leaves the most time for others.
Start by selecting the first activity in the sorted list (earliest finish). Then, skip all activities that start before this one ends. Repeat this process with the next compatible activity.
Result
You get a set of activities that do not overlap and are as many as possible.
Understanding that earliest finish time maximizes remaining time is key to the greedy approach.
4
IntermediateImplementing Activity Selection in C#
🤔Before reading on: do you think a simple loop checking start and end times is enough to select activities? Commit to your answer.
Concept: Use a loop to pick activities based on sorted finish times and compatibility.
Code example: using System; using System.Collections.Generic; class Activity { public int Start, Finish; public Activity(int s, int f) { Start = s; Finish = f; } } class Program { static List SelectActivities(List activities) { activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); List selected = new List(); int lastFinish = -1; foreach (var act in activities) { if (act.Start >= lastFinish) { selected.Add(act); lastFinish = act.Finish; } } return selected; } static void Main() { var acts = new List { new Activity(1, 4), new Activity(3, 5), new Activity(0, 6), new Activity(5, 7), new Activity(8, 9), new Activity(5, 9) }; var result = SelectActivities(acts); foreach (var a in result) { Console.WriteLine($"Activity: {a.Start} - {a.Finish}"); } } }
Result
Output: Activity: 1 - 4 Activity: 5 - 7 Activity: 8 - 9
Knowing how to implement the greedy logic in code solidifies the algorithm's practical use.
5
IntermediateAnalyzing Time Complexity
🤔
Concept: Understand how sorting and selection affect performance.
Sorting the activities takes O(n log n) time. The selection loop runs in O(n) time. Overall, the algorithm runs in O(n log n) due to sorting.
Result
You can predict how the algorithm scales with input size.
Recognizing the bottleneck helps optimize or choose algorithms for large data.
6
AdvancedHandling Edge Cases and Equal Finish Times
🤔Before reading on: do you think activities with the same finish time can both be selected? Commit to your answer.
Concept: Decide how to handle activities that finish at the same time or start exactly when another ends.
If two activities finish at the same time, picking either first is fine. Activities starting exactly when another ends do not overlap and can be selected. The algorithm naturally handles these cases due to the >= condition.
Result
The algorithm correctly selects compatible activities even with equal finish times.
Understanding boundary conditions prevents subtle bugs in scheduling.
7
ExpertExtending to Weighted Activity Selection
🤔Before reading on: do you think the greedy approach works when activities have different values or weights? Commit to your answer.
Concept: When activities have weights or values, the greedy method may fail, requiring dynamic programming.
If each activity has a value, picking earliest finishing may not maximize total value. Instead, use dynamic programming to consider all combinations and pick the best total value. This is called the Weighted Interval Scheduling problem.
Result
You learn the limits of the greedy approach and when to use more complex methods.
Knowing when greedy fails and dynamic programming is needed deepens algorithmic understanding.
Under the Hood
The algorithm works by first sorting activities by their finish times, then iteratively selecting the next activity that starts after the last selected one finishes. This greedy choice ensures that at each step, the remaining time is maximized for future activities. Internally, sorting arranges data for efficient linear scanning, and the selection loop uses simple comparisons to maintain compatibility.
Why designed this way?
This approach was designed because choosing the earliest finishing activity leaves the largest possible time window for the rest, which is a locally optimal choice leading to a globally optimal solution. Alternatives like choosing the shortest activity or earliest start time do not guarantee optimal results. The greedy method is simple, efficient, and provably correct for this problem.
┌───────────────┐
│ Activity List │
├───────────────┤
│ Sort by Finish│
└──────┬────────┘
       │
       ▼
┌─────────────────────────────┐
│ Iterate activities in order  │
│ ┌─────────────────────────┐ │
│ │ If start >= last finish │─┼─► Select activity
│ └─────────────────────────┘ │
└─────────────────────────────┘
       │
       ▼
┌─────────────────────────────┐
│ Result: Max compatible set   │
└─────────────────────────────┘
Myth Busters - 3 Common Misconceptions
Quick: Does picking the longest activity first always maximize total activities? Commit yes or no.
Common Belief:Picking the longest activity first will maximize the number of activities attended.
Tap to reveal reality
Reality:Picking the longest activity first often reduces the total number of activities because it blocks more time, preventing other activities from fitting in.
Why it matters:Choosing the longest activity first can lead to fewer total activities, wasting opportunities to attend more.
Quick: Can activities that start exactly when another ends be selected together? Commit yes or no.
Common Belief:Activities that start exactly when another ends overlap and cannot both be selected.
Tap to reveal reality
Reality:Activities starting exactly when another ends do not overlap and can both be selected.
Why it matters:Misunderstanding this causes missing out on valid activities, reducing the total count.
Quick: Does the greedy approach work for weighted activities? Commit yes or no.
Common Belief:The greedy method always finds the best set, even if activities have different values.
Tap to reveal reality
Reality:The greedy approach fails when activities have weights; dynamic programming is needed to maximize total value.
Why it matters:Using greedy in weighted cases leads to suboptimal solutions, missing higher-value combinations.
Expert Zone
1
The greedy algorithm's correctness depends on the problem's structure; it works because the earliest finish time choice is safe and optimal.
2
In practice, stable sorting by finish time can affect which activities are chosen when finish times tie, impacting reproducibility.
3
The algorithm can be adapted to handle activities with additional constraints, like mandatory breaks, by modifying the selection condition.
When NOT to use
Do not use the greedy approach when activities have weights or values that matter; instead, use Weighted Interval Scheduling with dynamic programming. Also avoid greedy if activities have complex constraints like dependencies or resource limits.
Production Patterns
Used in scheduling meetings, CPU job scheduling, event planning, and booking systems where maximizing non-overlapping tasks is critical. Often combined with priority queues or segment trees for dynamic or online scheduling.
Connections
Greedy Algorithms
The Activity Selection Problem is a classic example of a greedy algorithm.
Understanding this problem helps grasp the core idea of greedy methods: making locally optimal choices to reach a global optimum.
Interval Graphs
Activities can be represented as intervals on a line, and selecting non-overlapping activities corresponds to finding an independent set in an interval graph.
Knowing graph theory concepts enriches understanding of scheduling and conflict resolution problems.
Resource Allocation in Operations Research
Activity selection models how limited resources (time, space) are allocated efficiently.
This connection shows how algorithms solve practical problems in logistics, manufacturing, and project management.
Common Pitfalls
#1Selecting activities based on earliest start time instead of earliest finish time.
Wrong approach:activities.Sort((a, b) => a.Start.CompareTo(b.Start)); List selected = new List(); int lastFinish = -1; foreach (var act in activities) { if (act.Start >= lastFinish) { selected.Add(act); lastFinish = act.Finish; } }
Correct approach:activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); List selected = new List(); int lastFinish = -1; foreach (var act in activities) { if (act.Start >= lastFinish) { selected.Add(act); lastFinish = act.Finish; } }
Root cause:Confusing earliest start with earliest finish ignores the key insight that finishing early frees time for more activities.
#2Using > instead of >= when checking if an activity can be selected.
Wrong approach:if (act.Start > lastFinish) { /* select activity */ }
Correct approach:if (act.Start >= lastFinish) { /* select activity */ }
Root cause:Misunderstanding that activities starting exactly when another ends do not overlap causes missing valid selections.
#3Applying greedy approach to weighted activities expecting maximum total value.
Wrong approach:// Greedy selection ignoring weights activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); // select activities greedily without considering weights
Correct approach:// Use dynamic programming for weighted activities // Compute optimal weighted set considering compatibility
Root cause:Assuming greedy works for all scheduling problems without considering weights leads to incorrect solutions.
Key Takeaways
The Activity Selection Problem teaches how to schedule the maximum number of non-overlapping activities by always picking the earliest finishing one first.
Sorting activities by finish time is the foundation that enables a simple and efficient greedy selection.
The greedy approach works because choosing the earliest finishing activity leaves the most room for others, guaranteeing an optimal solution.
This problem highlights the limits of greedy algorithms, especially when activities have weights, requiring more advanced methods like dynamic programming.
Understanding this problem builds a strong base for learning more complex scheduling and optimization problems in computer science and real life.

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 time
D. Calculate the total duration of all activities

Solution

  1. Step 1: Understand the problem goal

    The Activity Selection Problem aims to choose activities that do not overlap and maximize the count.
  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 count without time conflicts [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 C# code snippets correctly sorts activities by their finish time for the Activity Selection Problem?
easy
A. activities.Sort((a, b) => a.Start.CompareTo(b.Start));
B. activities.Sort((a, b) => a.Finish.CompareTo(b.Finish));
C. activities.OrderBy(a => a.Duration);
D. activities.OrderByDescending(a => a.Start);

Solution

  1. Step 1: Identify sorting criteria for Activity Selection

    The greedy approach requires sorting activities by their finish time in ascending order.
  2. Step 2: Match code with sorting by finish time

    activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); uses Sort with finish time comparison, which is correct.
  3. Final Answer:

    activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); -> Option B
  4. Quick Check:

    Sort by finish time = activities.Sort((a, b) => a.Finish.CompareTo(b.Finish)); [OK]
Hint: Sort activities by finish time ascending [OK]
Common Mistakes:
  • Sorting by start time instead of finish time
  • Using OrderBy without assignment
  • Sorting descending instead of ascending
3. Given the following activities with start and finish times:
var activities = new List<Activity> { new Activity(1, 4), new Activity(3, 5), new Activity(0, 6), new Activity(5, 7), new Activity(8, 9) };
What is the maximum number of non-overlapping activities selected by the greedy algorithm?
medium
A. 5
B. 4
C. 2
D. 3

Solution

  1. Step 1: Sort activities by finish time

    Sorted activities by finish time: (1,4), (3,5), (0,6), (5,7), (8,9)
  2. Step 2: Select activities greedily

    Select (1,4) first, skip (3,5) and (0,6) because they overlap, select (5,7), then (8,9). Total selected = 3.
  3. Final Answer:

    3 -> Option D
  4. Quick Check:

    Max non-overlapping = 3 [OK]
Hint: Pick earliest finishing activities first [OK]
Common Mistakes:
  • Counting overlapping activities
  • Not sorting by finish time
  • Selecting activities by start time only
4. Consider this buggy C# code for Activity Selection:
activities.Sort((a, b) => a.Start.CompareTo(b.Start));
var selected = new List<Activity>();
selected.Add(activities[0]);
foreach(var act in activities) {
  if(act.Start >= selected.Last().Finish) {
    selected.Add(act);
  }
}

What is the main error causing incorrect results?
medium
A. Looping over activities incorrectly
B. Using >= instead of > in condition
C. Sorting by start time instead of finish time
D. Not initializing selected list before adding

Solution

  1. Step 1: Check sorting criteria

    The code sorts activities by start time, but greedy algorithm requires sorting by finish time.
  2. Step 2: Understand impact of wrong sorting

    Sorting by start time can select overlapping activities, causing incorrect results.
  3. Final Answer:

    Sorting by start time instead of finish time -> Option C
  4. Quick Check:

    Sort by finish time needed, not start time [OK]
Hint: Always sort by finish time for correct selection [OK]
Common Mistakes:
  • Sorting by start time
  • Confusing condition operators
  • Forgetting to add first activity
5. You have activities with start and finish times:
[(1,3), (2,5), (4,7), (6,9), (8,10), (9,11)]
Using the Activity Selection Problem approach, which set of activities maximizes the number of non-overlapping activities?
hard
A. [(1,3), (4,7), (8,10)]
B. [(1,3), (4,7), (6,9), (9,11)]
C. [(1,3), (6,9), (9,11)]
D. [(2,5), (6,9), (9,11)]

Solution

  1. Step 1: Sort activities by finish time

    Sorted by finish time: (1,3), (2,5), (4,7), (6,9), (8,10), (9,11)
  2. Step 2: Select activities greedily

    Select (1,3), skip (2,5) because it overlaps with (1,3), select (4,7) (4 >= 3), select (6,9) overlaps with (4,7)? No, 6 < 7 so skip (6,9), select (8,10) (8 >= 7), select (9,11) overlaps with (8,10)? 9 < 10 so skip (9,11). But this conflicts with options, so let's re-check carefully.
  3. Step 2 corrected:

    Select (1,3), skip (2,5), select (4,7), skip (6,9) (6 < 7), select (8,10), skip (9,11) (9 < 10). Total 3 activities: (1,3), (4,7), (8,10).
  4. Step 3: Verify options

    [(1,3), (4,7), (8,10)] matches the selected activities: [(1,3), (4,7), (8,10)].
  5. Final Answer:

    [(1,3), (4,7), (8,10)] -> Option A
  6. Quick Check:

    Greedy selection = [(1,3), (4,7), (8,10)] [OK]
Hint: Pick earliest finishing compatible activities stepwise [OK]
Common Mistakes:
  • Choosing overlapping activities
  • Ignoring sorting by finish time
  • Selecting more activities that overlap
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