Open In App

Job Sequencing Problem using Disjoint Set

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Given a set of n jobs where each job i has a deadline di >=1 and profit pi>=0. Only one job can be scheduled at a time. Each job takes 1 unit of time to complete. We earn the profit if and only if the job is completed by its deadline. The task is to find the subset of jobs that maximizes profit.

Examples: 

Input: Four Jobs with following deadlines and profits
JobID Deadline Profit
   a      4      20
   b      1      10
   c      1      40
   d      1      30
Output: Following is maximum profit sequence of jobs:
       c, a

Input: Five Jobs with following deadlines and profits
JobID Deadline Profit
   a     2       100
   b     1       19
   c     2       27
   d     1       25
   e     3       15
Output: Following is maximum profit sequence of jobs:
       c, a, e

A greedy solution of time complexity O(n2) is already discussed here. Below is the simple Greedy Algorithm.

  1. Sort all jobs in decreasing order of profit.
  2. Initialize the result sequence as first job in sorted jobs.
  3. Do following for remaining n-1 jobs 
    • If the current job can fit in the current result sequence without missing the deadline, add current job to the result. Else ignore the current job.

The costly operation in the Greedy solution is to assign a free slot for a job. We were traversing each and every slot for a job and assigning the greatest possible time slot(<deadline) which was available.
What does greatest time slot means? 
Suppose that a job J1 has a deadline of time t = 5. We assign the greatest time slot which is free and less than the deadline i.e 4-5 for this job. Now another job J2 with deadline of 5 comes in, so the time slot allotted will be 3-4 since 4-5 has already been allotted to job J1.
Why to assign greatest time slot(free) to a job? 
Now we assign the greatest possible time slot since if we assign a time slot even lesser than the available one then there might be some other job which will miss its deadline. 

Example: 
J1 with deadline d1 = 5, profit 40 
J2 with deadline d2 = 1, profit 20 
Suppose that for job J1 we assigned time slot of 0-1. Now job J2 cannot be performed since we will perform Job J1 during that time slot.
Using Disjoint Set for Job Sequencing 
All time slots are individual sets initially. We first find the maximum deadline of all jobs. Let the max deadline be m. We create m+1 individual sets. If a job is assigned a time slot of t where t >= 0, then the job is scheduled during [t-1, t]. So a set with value X represents the time slot [X-1, X]. 
We need to keep track of the greatest time slot available which can be allotted to a given job having deadline. We use the parent array of Disjoint Set Data structures for this purpose. The root of the tree is always the latest available slot. If for a deadline d, there is no slot available, then root would be 0. Below are detailed steps.
Initialize Disjoint Set: Creates initial disjoint sets.

// m is maximum deadline of a job
parent = new int[m + 1];

// Every node is a parent of itself
for (int i = 0; i ? m; i++)
    parent[i] = i;

Find : Finds the latest time slot available. 

// Returns the maximum available time slot
find(s)
{
    // Base case
    if (s == parent[s])
       return s;

    // Recursive call with path compression
    return parent[s] = find(parent[s]);
} 

Union : 

 Merges two sets.  
// Makes u as parent of v.
union(u, v)
{
   // update the greatest available
   // free slot to u
   parent[v] = u;
} 

How come find returns the latest available time slot? 
Initially, all time slots are individual slots. So the time slot returned is always maximum. When we assign a time slot ‘t’ to a job, we do union of ‘t’ with ‘t-1’ in a way that ‘t-1’ becomes the parent of ‘t’. To do this we call union(t-1, t). This means that all future queries for time slot t would now return the latest time slot available for set represented by t-1.

Implementation : 
The following is the implementation of above algorithm.

C++




// C++ Program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
#include<bits/stdc++.h>
using namespace std;
 
// A structure to represent various attributes of a Job
struct Job
{
    // Each job has id, deadline and profit
    char id;
    int deadLine, profit;
};
 
// A Simple Disjoint Set Data Structure
struct DisjointSet
{
    int *parent;
 
    // Constructor
    DisjointSet(int n)
    {
        parent = new int[n+1];
 
        // Every node is a parent of itself
        for (int i = 0; i <= n; i++)
            parent[i] = i;
    }
 
    // Path Compression
    int find(int s)
    {
        /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
        if (s == parent[s])
            return s;
        return parent[s] = find(parent[s]);
    }
 
    // Makes u as parent of v.
    void merge(int u, int v)
    {
        //update the greatest available
        //free slot to u
        parent[v] = u;
    }
};
 
// Used to sort in descending order on the basis
// of profit for each job
bool cmp(Job a, Job b)
{
    return (a.profit > b.profit);
}
 
// Functions returns the maximum deadline from the set
// of jobs
int findMaxDeadline(struct Job arr[], int n)
{
    int ans = INT_MIN;
    for (int i = 0; i < n; i++)
        ans = max(ans, arr[i].deadLine);
    return ans;
}
 
int printJobScheduling(Job arr[], int n)
{
    // Sort Jobs in descending order on the basis
    // of their profit
    sort(arr, arr + n, cmp);
 
    // Find the maximum deadline among all jobs and
    // create a disjoint set data structure with
    // maxDeadline disjoint sets initially.
    int maxDeadline = findMaxDeadline(arr, n);
    DisjointSet ds(maxDeadline);
 
    // Traverse through all the jobs
    for (int i = 0; i < n; i++)
    {
        // Find the maximum available free slot for
        // this job (corresponding to its deadline)
        int availableSlot = ds.find(arr[i].deadLine);
 
        // If maximum available free slot is greater
        // than 0, then free slot available
        if (availableSlot > 0)
        {
            // This slot is taken by this job 'i'
            // so we need to update the greatest
            // free slot. Note that, in merge, we
            // make first parameter as parent of
            // second parameter. So future queries
            // for availableSlot will return maximum
            // available slot in set of
            // "availableSlot - 1"
            ds.merge(ds.find(availableSlot - 1),
                             availableSlot);
 
            cout << arr[i].id << " ";
        }
    }
}
 
// Driver code
int main()
{
    Job arr[] =  { { 'a', 2, 100 }, { 'b', 1, 19 },
                   { 'c', 2, 27 },  { 'd', 1, 25 },
                   { 'e', 3, 15 } };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Following jobs need to be "
         << "executed for maximum profit\n";
    printJobScheduling(arr, n);
    return 0;
}


Java




// Java program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
import java.util.*;
 
// A Simple Disjoint Set Data Structure
class DisjointSet
{
    int parent[];
 
    // Constructor
    DisjointSet(int n)
    {
        parent = new int[n + 1];
 
        // Every node is a parent of itself
        for (int i = 0; i <= n; i++)
            parent[i] = i;
    }
 
    // Path Compression
    int find(int s)
    {
        /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
        if (s == parent[s])
            return s;
        return parent[s] = find(parent[s]);
    }
 
    // Makes u as parent of v.
    void merge(int u, int v)
    {
        //update the greatest available
        //free slot to u
        parent[v] = u;
    }
}
 
class Job implements Comparator<Job>
{
    // Each job has a unique-id, profit and deadline
    char id;
    int deadline, profit;
 
    // Constructors
    public Job() { }
    public Job(char id,int deadline,int profit)
    {
        this.id = id;
        this.deadline = deadline;
        this.profit = profit;
    }
 
    // Returns the maximum deadline from the set of jobs
    public static int findMaxDeadline(ArrayList<Job> arr)
    {
        int ans = Integer.MIN_VALUE;
        for (Job temp : arr)
            ans = Math.max(temp.deadline, ans);
        return ans;
    }
 
    // Prints optimal job sequence
    public static void printJobScheduling(ArrayList<Job> arr)
    {
        // Sort Jobs in descending order on the basis
        // of their profit
        Collections.sort(arr, new Job());
 
        // Find the maximum deadline among all jobs and
        // create a disjoint set data structure with
        // maxDeadline disjoint sets initially.
        int maxDeadline = findMaxDeadline(arr);
        DisjointSet dsu = new DisjointSet(maxDeadline);
 
        // Traverse through all the jobs
        for (Job temp : arr)
        {
            // Find the maximum available free slot for
            // this job (corresponding to its deadline)
            int availableSlot = dsu.find(temp.deadline);
 
 
            // If maximum available free slot is greater
            // than 0, then free slot available
            if (availableSlot > 0)
            {
                // This slot is taken by this job 'i'
                // so we need to update the greatest free
                // slot. Note that, in merge, we make
                // first parameter as parent of second
                // parameter.  So future queries for
                // availableSlot will return maximum slot
                // from set of "availableSlot - 1"
                dsu.merge(dsu.find(availableSlot - 1),
                                   availableSlot);
                System.out.print(temp.id + " ");
            }
        }
        System.out.println();
    }
 
    // Used to sort in descending order on the basis
    // of profit for each job
    public int compare(Job j1, Job j2)
    {
        return j1.profit > j2.profit? -1: 1;
    }
}
 
// Driver code
class Main
{
    public static void main(String args[])
    {
        ArrayList<Job> arr=new ArrayList<Job>();
        arr.add(new Job('a',2,100));
        arr.add(new Job('b',1,19));
        arr.add(new Job('c',2,27));
        arr.add(new Job('d',1,25));
        arr.add(new Job('e',3,15));
        System.out.println("Following jobs need to be "+
                           "executed for maximum profit");
        Job.printJobScheduling(arr);
    }
}


C#




// C# program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
using System;
using System.Collections.Generic;
 
// A Simple Disjoint Set Data Structure
public class DisjointSet
{
  public int[] parent;
 
  // Constructor
  public DisjointSet(int n)
  {
    parent = new int[n + 1];
 
    // Every node is a parent of itself
    for (int i = 0; i <= n; i++)
      parent[i] = i;
  }
 
  // Path Compression
  public int find(int s)
  {
    /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
    if (s == parent[s])
      return s;
    return parent[s] = find(parent[s]);
  }
 
  // Makes u as parent of v.
  public void merge(int u, int v)
  {
    //update the greatest available
    //free slot to u
    parent[v] = u;
  }
}
 
public class Job:Comparer<Job>
{
    // Each job has a unique-id, profit and deadline
    public char id;
    public int deadline, profit;
 
    // Constructors
    public Job() { }
    public Job(char id,int deadline,int profit)
    {
        this.id = id;
        this.deadline = deadline;
        this.profit = profit;
    }
 
    // Returns the maximum deadline from the set of jobs
    public static int findMaxDeadline(List<Job> arr)
    {
        int ans = Int32.MinValue;
        for (int i=0;i<arr.Count;i++){
            Job temp = arr[i];
            ans = Math.Max(temp.deadline, ans);
        }
        return ans;
    }
 
    // Prints optimal job sequence
    public static void printJobScheduling(List<Job> arr)
    {
        // Sort Jobs in descending order on the basis
        // of their profit
        //Collections.sort(arr, new Job());
        arr.Sort(new Job());
 
        // Find the maximum deadline among all jobs and
        // create a disjoint set data structure with
        // maxDeadline disjoint sets initially.
        int maxDeadline = findMaxDeadline(arr);
        DisjointSet dsu = new DisjointSet(maxDeadline);
 
        // Traverse through all the jobs
        for (int i=0;i<arr.Count;i++)
        {
            Job temp = arr[i];
 
            // Find the maximum available free slot for
            // this job (corresponding to its deadline)
            int availableSlot = dsu.find(temp.deadline);
 
 
            // If maximum available free slot is greater
            // than 0, then free slot available
            if (availableSlot > 0)
            {
                // This slot is taken by this job 'i'
                // so we need to update the greatest free
                // slot. Note that, in merge, we make
                // first parameter as parent of second
                // parameter.  So future queries for
                // availableSlot will return maximum slot
                // from set of "availableSlot - 1"
                dsu.merge(dsu.find(availableSlot - 1),availableSlot);
                Console.Write(temp.id + " ");
            }
        }
        Console.Write("\n");
    }
 
    // Used to sort in descending order on the basis
    // of profit for each job
    public override int Compare(Job j1, Job j2)
    {
        return j1.profit > j2.profit? -1: 1;
    }
}
 
 
public class GFG{
    static public void Main (){
        //Code
        List<Job> arr=new List<Job>();
        arr.Add(new Job('a',2,100));
        arr.Add(new Job('b',1,19));
        arr.Add(new Job('c',2,27));
        arr.Add(new Job('d',1,25));
        arr.Add(new Job('e',3,15));
        Console.Write("Following jobs need to be executed for maximum profit");
        Console.Write("\n");
        Job.printJobScheduling(arr);
    }
}
 
// This code is contributed by shruti456rawal


Python3




# Python3 program to find the maximum profit
# job sequence from a given array of jobs
# with deadlines and profits
import sys
 
class DisjointSet:
    def __init__(self, n):
        self.parent = [i for i in range(n + 1)]
 
    def find(self, s):
     
        # Make the parent of nodes in the path from
        # u --> parent[u] point to parent[u]
        if s == self.parent[s]:
            return s
        self.parent[s] = self.find(self.parent[s])
        return self.parent[s]
 
    # Make us as parent of v
    def merge(self, u, v):
         
        # Update the greatest available
        # free slot to u
        self.parent[v] = u
 
def cmp(a):
    return a['profit']
 
def findmaxdeadline(arr, n):
    """
    :param arr: Job array
    :param n: length of array
    :return: maximum deadline from the set of jobs
    """
    ans = - sys.maxsize - 1
    for i in range(n):
        ans = max(ans, arr[i]['deadline'])
    return ans
 
def printjobscheduling(arr, n):
     
    # Sort jobs in descending order on
    # basis of their profit
    arr = sorted(arr, key = cmp, reverse = True)
 
    """
    Find the maximum deadline among all jobs and
    create a disjoint set data structure with
    max_deadline disjoint sets initially
    """
    max_deadline = findmaxdeadline(arr, n)
    ds = DisjointSet(max_deadline)
 
    for i in range(n):
 
        # find maximum available free slot for
        # this job (corresponding to its deadline)
        available_slot = ds.find(arr[i]['deadline'])
        if available_slot > 0:
 
            # This slot is taken by this job 'i'
            # so we need to update the greatest free slot.
            # Note: In merge, we make first parameter
            # as parent of second parameter.
            # So future queries for available_slot will
            # return maximum available slot in set of
            # "available_slot - 1"
            ds.merge(ds.find(available_slot - 1),
                             available_slot)
            print(arr[i]['id'], end = " ")
 
# Driver Code
if __name__ == "__main__":
    arr = [{'id': 'a', 'deadline': 2, 'profit': 100},
           {'id': 'b', 'deadline': 1, 'profit': 19},
           {'id': 'c', 'deadline': 2, 'profit': 27},
           {'id': 'd', 'deadline': 1, 'profit': 25},
           {'id': 'e', 'deadline': 3, 'profit': 15}]
    n = len(arr)
    print("Following jobs need to be",
          "executed for maximum profit")
    printjobscheduling(arr, n)
 
# This code is contributed by Rajat Srivastava


Javascript




// JS program to find the maximum profit job sequence
// from a given array of jobs with deadlines and profits
class DisjointSet {
      // Constructor
  constructor(n) {
       // Every node is a parent of itself
    this.parent = Array.from({ length: n + 1 }, (_, i) => i);
  }
 // Path Compression
  find(s) {
       /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
    if (s === this.parent[s]) {
      return s;
    }
    this.parent[s] = this.find(this.parent[s]);
    return this.parent[s];
  }
    // Makes u as parent of v.
  merge(u, v) {
        //update the greatest available
        //free slot to u
    this.parent[v] = u;
  }
}
 
    // Used to sort in descending order on the basis
    // of profit for each job
function cmp(a, b) {
  return b.profit - a.profit;
}
   // Returns the maximum deadline from the set of jobs
function findmaxdeadline(arr, n) {
  let ans = -Infinity;
  for (let i = 0; i < n; i++) {
    ans = Math.max(ans, arr[i].deadline);
  }
  return ans;
}
   // Prints optimal job sequence
function printjobscheduling(arr, n) {
     
        // Sort Jobs in descending order on the basis
        // of their profit
  arr.sort(cmp);
    // Find the maximum deadline among all jobs and
        // create a disjoint set data structure with
        // maxDeadline disjoint sets initially.
  const max_deadline = findmaxdeadline(arr, n);
  const ds = new DisjointSet(max_deadline);
 
        // Traverse through all the jobs
  for (let i = 0; i < n; i++) {
      // Find the maximum available free slot for
            // this job (corresponding to its deadline)
    const available_slot = ds.find(arr[i].deadline);
     // If maximum available free slot is greater
            // than 0, then free slot available
    if (available_slot > 0) {
         // This slot is taken by this job 'i'
                // so we need to update the greatest free
                // slot. Note that, in merge, we make
                // first parameter as parent of second
                // parameter.  So future queries for
                // availableSlot will return maximum slot
                // from set of "availableSlot - 1"
      ds.merge(ds.find(available_slot - 1), available_slot);
      console.log(arr[i].id + ' ');
    }
  }
}
 
// Driver code
const arr = [
  { id: 'a', deadline: 2, profit: 100 },
  { id: 'b', deadline: 1, profit: 19 },
  { id: 'c', deadline: 2, profit: 27 },
  { id: 'd', deadline: 1, profit: 25 },
  { id: 'e', deadline: 3, profit: 15 },
];
const n = arr.length;
 
console.log('Following jobs need to be executed for maximum profit'+'<br>');
printjobscheduling(arr, n);
 
// This code is contributed by lokeshpotta20.


Output

Following jobs need to be executed for maximum profit
a c e 

Time Complexity: O(N*log(D))  where N is the total number of jobs and D is the maximum possible deadline.
Space Complexity: O(D) Since, an extra array was created for disjoint set with a size of D.

 

 



Last Updated : 15 Feb, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads