Open In App

Activity Selection Problem | Greedy Algo-1

Last Updated : 29 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Given n activities with start times in start[] and finish times in finish[], find the maximum number of activities a single person can perform without overlap. A person can only do one activity at a time.

Examples:  

Input: start[]  =  [1, 3, 0, 5, 8, 5], finish[] =  [2, 4, 6, 7, 9, 9]
Output: 4
Explanation: A person can perform at most four activities. The maximum set of activities that can be performed is {0, 1, 3, 4} (these are the indexes in the start[] and finish[] arrays).

Input: start[]  =  [10, 12, 20], finish[] =  [20, 25, 30]
Output: 1
Explanation: A person can perform at most one activity.

[Naive Approach] Generate All Subsets - O(n^2 * 2^n) Time and O(n) Space

Generates all possible subsets of activities, where each subset represents a potential selection. For each subset, we check if the activities are mutually non-overlapping by comparing their time intervals pairwise. If a subset is valid and contains more activities than our current maximum, we update the maximum count. In the end, we get the size of the largest valid subset.

How does Greedy Choice work for Activities sorted according to finish time? 

Let the set of activities be S={1,2,3,…,n} sorted by their finish times. The greedy strategy always selects activity 1 first (the one with the earliest finish time).

Why does activity 1 always lead to an optimal solution?

We can prove this by contradiction: suppose there exists another optimal solution B whose first selected activity is k≠1 Since the activities in B are non-overlapping and k is the earliest finishing activity in B, it follows that:

finish(k) ≥ finish(1)

Now, we can construct a new solution A by replacing k with activity 1 in B:

Let A = (B∖{k}) ∪ {1}

This means: we form set A by taking all activities in B except k, and adding activity 1 instead.

Because activity 1 finishes no later than k, replacing k with 1 does not create any overlaps. Thus, A is also a valid solution with the same size as B, but it starts with activity 1.

This shows that there always exists an optimal solution that begins with the activity that finishes earliest (activity 1).

[Expected Approach 1] - Using Sorting - O(n * log(n)) Time and O(n) Space

The greedy strategy is to always pick the next activity that has the earliest finish time among the remaining activities and starts after the previously selected activity finishes. By sorting the activities based on their finish times, we ensure that at each step, we select the activity with the minimum finishing time available.

Step By Step Implementations:

  • Sort the activities according to their finishing time 
  • Select the first activity from the sorted array 
  • Do the following for the remaining activities in the sorted array
    • If the start time of this activity is greater than or equal to the finish time of the previously selected activity then select this activity


C++
// C++ program for activity selection problem
#include <bits/stdc++.h>
using namespace std;

// Function to solve the activity selection problem
int activitySelection(vector<int> &start, vector<int> &finish)
{
    vector<vector<int>> arr;
    for (int i = 0; i < start.size(); i++) {
        arr.push_back({start[i], finish[i]});
    }

    // Sort activities by finish time
    sort(arr.begin(), arr.end(),
    [](const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    });
    
    // At least one activity can be performed
    int count = 1;  
    
    // Index of last selected activity
    int j = 0;      

    for (int i = 1; i < arr.size(); i++) {
       
        // Check if current activity starts
        // after last selected activity finishes
        if (arr[i][0] > arr[j][1]) {
            count++;
            
            // Update last selected activity
            j = i;  
        }
    }

    return count;
}

int main()
{
    vector<int> start = {1, 3, 0, 5, 8, 5};
    vector<int> finish = {2, 4, 6, 7, 9, 9};
    cout << activitySelection(start, finish);
    return 0;
}
Java
// Java program for activity selection problem
import java.util.*;

public class GfG {
    
    // Function to solve the activity selection problem
    public static int activitySelection(int[] start, int[] finish) {
        int n = start.length;
        int[][] arr = new int[n][2];
        for (int i = 0; i < n; i++) {
            arr[i][0] = start[i];
            arr[i][1] = finish[i];
        }

        // Sort activities by finish time
        Arrays.sort(arr, Comparator.comparingInt(a -> a[1]));
        
        // At least one activity can be performed
        int count = 1;
        
        // Index of last selected activity
        int j = 0;

        for (int i = 1; i < n; i++) {
            
            // Check if current activity starts
            // after last selected activity finishes
            if (arr[i][0] > arr[j][1]) {
                count++;
                
                // Update last selected activity
                j = i;
            }
        }

        return count;
    }

    public static void main(String[] args) {
        int[] start = {1, 3, 0, 5, 8, 5};
        int[] finish = {2, 4, 6, 7, 9, 9};
        System.out.println(activitySelection(start, finish));
    }
}
Python
# Python program for activity selection problem

def activitySelection(start, finish):
    arr = list(zip(start, finish))

    # Sort activities by finish time
    arr.sort(key=lambda x: x[1])
    
    # At least one activity can be performed
    count = 1
    
    # Index of last selected activity
    j = 0

    for i in range(1, len(arr)):
        
        # Check if current activity starts
        # after last selected activity finishes
        if arr[i][0] > arr[j][1]:
            count += 1
            
            # Update last selected activity
            j = i

    return count

if __name__ == '__main__':
    start = [1, 3, 0, 5, 8, 5]
    finish = [2, 4, 6, 7, 9, 9]
    print(activitySelection(start, finish))
C#
// Function to solve the activity selection problem
int activitySelection(int[] start, int[] finish)
{
    List<List<int>> arr = new List<List<int>>();
    for (int i = 0; i < start.Length; i++) {
        arr.Add(new List<int> { start[i], finish[i] });
    }

    // Sort activities by finish time
    arr.Sort((a, b) => a[1].CompareTo(b[1]));
    
    // At least one activity can be performed
    int count = 1;  
    
    // Index of last selected activity
    int j = 0;      

    for (int i = 1; i < arr.Count; i++) {
        
        // Check if current activity starts
        // after last selected activity finishes
        if (arr[i][0] > arr[j][1]) {
            count++;
            
            // Update last selected activity
            j = i;  
        }
    }

    return count;
}

public static void Main()
{
    int[] start = new int[] { 1, 3, 0, 5, 8, 5 };
    int[] finish = new int[] { 2, 4, 6, 7, 9, 9 };
    Console.WriteLine(activitySelection(start, finish));
}
JavaScript
// Function to solve the activity selection problem
function activitySelection(start, finish) {
    let arr = [];
    for (let i = 0; i < start.length; i++) {
        arr.push([start[i], finish[i]]);
    }

    // Sort activities by finish time
    arr.sort((a, b) => a[1] - b[1]);
    
    // At least one activity can be performed
    let count = 1;  
    
    // Index of last selected activity
    let j = 0;      

    for (let i = 1; i < arr.length; i++) {
        
        // Check if current activity starts
        // after last selected activity finishes
        if (arr[i][0] > arr[j][1]) {
            count++;
            
            // Update last selected activity
            j = i;  
        }
    }

    return count;
}

const start = [1, 3, 0, 5, 8, 5];
const finish = [2, 4, 6, 7, 9, 9];
console.log(activitySelection(start, finish));

Output
4

[Expected Approach 2] - Using Priority Queue - O(n * log(n)) Time and O(n) Space

We can use Min-Heap to get the activity with minimum finish time. Min-Heap can be implemented using priority-queue

Step By Step Implementations:

  • Create a priority queue (min-heap) and push all activities into it, prioritized by their finish times.
  • Pop the top activity from the priority queue and add it to the answer vector. Set finish to the finish time of this activity.
  • While the priority queue is not empty, do the following:
    • Take the top activity from the priority queue.
    • If the start time of this activity is greater than or equal to finish, add it to the answer vector and update finish to this activity’s finish time.
    • Otherwise, ignore the activity.
  • After processing all activities, print the selected activities stored in the answer vector.
C++
// C++ program for activity selection problem
// when input activities may not be sorted.
#include <bits/stdc++.h>
using namespace std;

// Function to solve the activity selection problem
int activitySelection(vector<int> &start, vector<int> &finish)
{

    // to store results.
    int ans = 0;

    // Minimum Priority Queue to sort activities in
    // ascending order of finishing time (end[i]).
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> p;

    for (int i = 0; i < start.size(); i++)
    {
        p.push(make_pair(finish[i], start[i]));
    }

    // to store the end time of last activity
    int finishtime = -1;

    while (!p.empty())
    {
        pair<int, int> activity = p.top();
        p.pop();
        if (activity.second > finishtime)
        {
            finishtime = activity.first;
            ans++;
        }
    }
    return ans;
}

int main()
{
    vector<int> start = {1, 3, 0, 5, 8, 5};
    vector<int> finish = {2, 4, 6, 7, 9, 9};
    cout << activitySelection(start, finish);
    return 0;
}
Java
import java.util.PriorityQueue;

class GfG {

    // Function to solve the activity selection problem
    static int activitySelection(int[] start, int[] finish)
    {
        int n = start.length;
        int ans = 0;

        // Min Heap to store activities in ascending order
        // of finish time
        PriorityQueue<int[]> p = new PriorityQueue<>(
            (a, b) -> Integer.compare(a[0], b[0]));

        for (int i = 0; i < n; i++) {
            p.add(new int[] { finish[i], start[i] });
        }

        // Variable to store the end time of the last
        // selected activity
        int finishtime = -1;

        while (!p.isEmpty()) {
            int[] activity
                = p.poll(); // Extract the activity with the
                            // smallest finish time
            if (activity[1] > finishtime) {
                finishtime = activity[0];
                ans++;
            }
        }

        return ans;
    }

    public static void main(String[] args)
    {
        int[] start = { 1, 3, 0, 5, 8, 5 };
        int[] finish = { 2, 4, 6, 7, 9, 9 };

        System.out.println(
            activitySelection(start, finish));
    }
}
Python
# Python program for activity selection problem
# when input activities may not be sorted.
import heapq

# Function to solve the activity selection problem


def activitySelection(start, finish):

    # to store results.
    ans = 0

    # Minimum Priority Queue to sort activities in
    # ascending order of finishing time (end[i]).
    p = []
    for i in range(len(start)):
        heapq.heappush(p, (finish[i], start[i]))

    # to store the end time of last activity
    finishtime = -1

    while p:
        activity = heapq.heappop(p)
        if activity[1] > finishtime:
            finishtime = activity[0]
            ans += 1

    return ans


if __name__ == "__main__":
    start = [1, 3, 0, 5, 8, 5]
    finish = [2, 4, 6, 7, 9, 9]
    print(activitySelection(start, finish))
C#
using System;

class GfG {
    // Custom Min-Heap for sorting activities by finish time
    class MinHeap {
        private int[, ] heap;
        private int size;

        public MinHeap(int capacity)
        {
            heap
                = new int[capacity, 2]; // 2D array to store
                                        // (finish, start)
            size = 0;
        }

        // Insert (finish, start) into heap
        public void Insert(int finish, int start)
        {
            heap[size, 0] = finish;
            heap[size, 1] = start;
            size++;
            HeapifyUp(size - 1);
        }

        // Extract min (smallest finish time)
        public(int, int) ExtractMin()
        {
            if (size == 0)
                return (-1, -1); // Empty heap

            int minFinish = heap[0, 0];
            int minStart = heap[0, 1];

            // Move last element to root
            heap[0, 0] = heap[size - 1, 0];
            heap[0, 1] = heap[size - 1, 1];
            size--;

            HeapifyDown(0); // Restore heap property

            return (minFinish, minStart);
        }

        public bool IsEmpty() { return size == 0; }

        // Maintain heap property (heapify up)
        private void HeapifyUp(int index)
        {
            while (index > 0) {
                int parent = (index - 1) / 2;
                if (heap[index, 0] < heap[parent, 0]) {
                    Swap(index, parent);
                    index = parent;
                }
                else
                    break;
            }
        }

        // Maintain heap property (heapify down)
        private void HeapifyDown(int index)
        {
            while (true) {
                int left = 2 * index + 1;
                int right = 2 * index + 2;
                int smallest = index;

                if (left < size
                    && heap[left, 0] < heap[smallest, 0])
                    smallest = left;
                if (right < size
                    && heap[right, 0] < heap[smallest, 0])
                    smallest = right;

                if (smallest != index) {
                    Swap(index, smallest);
                    index = smallest;
                }
                else
                    break;
            }
        }

        // Swap elements in heap
        private void Swap(int i, int j)
        {
            int tempFinish = heap[i, 0];
            int tempStart = heap[i, 1];

            heap[i, 0] = heap[j, 0];
            heap[i, 1] = heap[j, 1];

            heap[j, 0] = tempFinish;
            heap[j, 1] = tempStart;
        }
    }

    // Function to solve the Activity Selection Problem
    // using a Priority Queue (Min-Heap)
    static int activitySelection(int[] start, int[] finish)
    {
        int n = start.Length;
        int ans = 0;

        // Create Min-Heap (acts as Priority Queue)
        MinHeap heap = new MinHeap(n);

        // Insert all activities into the Min-Heap
        for (int i = 0; i < n; i++) {
            heap.Insert(finish[i], start[i]);
        }

        // Variable to track last selected activity's finish
        // time
        int finishtime = -1;

        // Process activities in increasing order of finish
        // time
        while (!heap.IsEmpty()) {
            (int finishTime, int startTime)
                = heap.ExtractMin();

            if (startTime > finishtime) {
                finishtime = finishTime;
                ans++;
            }
        }

        return ans;
    }

    static void Main()
    {
        int[] start = { 1, 3, 0, 5, 8, 5 };
        int[] finish = { 2, 4, 6, 7, 9, 9 };

        Console.WriteLine(activitySelection(start, finish));
    }
}
JavaScript
class MinHeap {
    constructor() { this.heap = []; }

    // Insert an element into the heap
    push(val)
    {
        this.heap.push(val);
        this.heapifyUp();
    }

    // Remove and return the smallest element
    pop()
    {
        if (this.heap.length === 1)
            return this.heap.pop();
        const min = this.heap[0];
        this.heap[0] = this.heap.pop();
        this.heapifyDown();
        return min;
    }

    // Heapify upwards (to maintain heap property after
    // insertion)
    heapifyUp()
    {
        let index = this.heap.length - 1;
        while (index > 0) {
            let parentIndex = Math.floor((index - 1) / 2);
            if (this.heap[parentIndex][0]
                <= this.heap[index][0])
                break;
            [this.heap[parentIndex], this.heap[index]] = [
                this.heap[index], this.heap[parentIndex]
            ];
            index = parentIndex;
        }
    }

    // Heapify downwards (to maintain heap property after
    // removal)
    heapifyDown()
    {
        let index = 0;
        while (true) {
            let left = 2 * index + 1;
            let right = 2 * index + 2;
            let smallest = index;

            if (left < this.heap.length
                && this.heap[left][0]
                       < this.heap[smallest][0]) {
                smallest = left;
            }
            if (right < this.heap.length
                && this.heap[right][0]
                       < this.heap[smallest][0]) {
                smallest = right;
            }
            if (smallest === index)
                break;
            [this.heap[index], this.heap[smallest]] =
                [ this.heap[smallest], this.heap[index] ];
            index = smallest;
        }
    }

    size() { return this.heap.length; }
}

// Function to solve the activity selection problem
function activitySelection(start, finish)
{
    let ans = 0;
    let minHeap = new MinHeap();

    // Insert all activities into the min heap
    for (let i = 0; i < start.length; i++) {
        minHeap.push([ finish[i], start[i] ]);
    }

    let finishtime = -1;

    // Process activities in order of finish time
    while (minHeap.size() > 0) {
        let activity
            = minHeap.pop(); // Get the activity with the
                             // smallest finish time
        if (activity[1] > finishtime) {
            finishtime = activity[0];
            ans++;
        }
    }

    return ans;
}

// Example usage
let start = [ 1, 3, 0, 5, 8, 5 ];
let finish = [ 2, 4, 6, 7, 9, 9 ];

console.log(activitySelection(start, finish)); // Output: 4

Output
4

Next Article

Similar Reads