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.
- Sort all jobs in decreasing order of profit.
- Initialize the result sequence as first job in sorted jobs.
- 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++
#include<bits/stdc++.h>
using namespace std;
struct Job
{
char id;
int deadLine, profit;
};
struct DisjointSet
{
int *parent;
DisjointSet( int n)
{
parent = new int [n+1];
for ( int i = 0; i <= n; i++)
parent[i] = i;
}
int find( int s)
{
if (s == parent[s])
return s;
return parent[s] = find(parent[s]);
}
void merge( int u, int v)
{
parent[v] = u;
}
};
bool cmp(Job a, Job b)
{
return (a.profit > b.profit);
}
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(arr, arr + n, cmp);
int maxDeadline = findMaxDeadline(arr, n);
DisjointSet ds(maxDeadline);
for ( int i = 0; i < n; i++)
{
int availableSlot = ds.find(arr[i].deadLine);
if (availableSlot > 0)
{
ds.merge(ds.find(availableSlot - 1),
availableSlot);
cout << arr[i].id << " " ;
}
}
}
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
import java.util.*;
class DisjointSet
{
int parent[];
DisjointSet( int n)
{
parent = new int [n + 1 ];
for ( int i = 0 ; i <= n; i++)
parent[i] = i;
}
int find( int s)
{
if (s == parent[s])
return s;
return parent[s] = find(parent[s]);
}
void merge( int u, int v)
{
parent[v] = u;
}
}
class Job implements Comparator<Job>
{
char id;
int deadline, profit;
public Job() { }
public Job( char id, int deadline, int profit)
{
this .id = id;
this .deadline = deadline;
this .profit = profit;
}
public static int findMaxDeadline(ArrayList<Job> arr)
{
int ans = Integer.MIN_VALUE;
for (Job temp : arr)
ans = Math.max(temp.deadline, ans);
return ans;
}
public static void printJobScheduling(ArrayList<Job> arr)
{
Collections.sort(arr, new Job());
int maxDeadline = findMaxDeadline(arr);
DisjointSet dsu = new DisjointSet(maxDeadline);
for (Job temp : arr)
{
int availableSlot = dsu.find(temp.deadline);
if (availableSlot > 0 )
{
dsu.merge(dsu.find(availableSlot - 1 ),
availableSlot);
System.out.print(temp.id + " " );
}
}
System.out.println();
}
public int compare(Job j1, Job j2)
{
return j1.profit > j2.profit? - 1 : 1 ;
}
}
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#
using System;
using System.Collections.Generic;
public class DisjointSet
{
public int [] parent;
public DisjointSet( int n)
{
parent = new int [n + 1];
for ( int i = 0; i <= n; i++)
parent[i] = i;
}
public int find( int s)
{
if (s == parent[s])
return s;
return parent[s] = find(parent[s]);
}
public void merge( int u, int v)
{
parent[v] = u;
}
}
public class Job:Comparer<Job>
{
public char id;
public int deadline, profit;
public Job() { }
public Job( char id, int deadline, int profit)
{
this .id = id;
this .deadline = deadline;
this .profit = profit;
}
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;
}
public static void printJobScheduling(List<Job> arr)
{
arr.Sort( new Job());
int maxDeadline = findMaxDeadline(arr);
DisjointSet dsu = new DisjointSet(maxDeadline);
for ( int i=0;i<arr.Count;i++)
{
Job temp = arr[i];
int availableSlot = dsu.find(temp.deadline);
if (availableSlot > 0)
{
dsu.merge(dsu.find(availableSlot - 1),availableSlot);
Console.Write(temp.id + " " );
}
}
Console.Write( "\n" );
}
public override int Compare(Job j1, Job j2)
{
return j1.profit > j2.profit? -1: 1;
}
}
public class GFG{
static public void Main (){
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);
}
}
|
Python3
import sys
class DisjointSet:
def __init__( self , n):
self .parent = [i for i in range (n + 1 )]
def find( self , s):
if s = = self .parent[s]:
return s
self .parent[s] = self .find( self .parent[s])
return self .parent[s]
def merge( self , u, v):
self .parent[v] = u
def cmp (a):
return a[ 'profit' ]
def findmaxdeadline(arr, n):
ans = - sys.maxsize - 1
for i in range (n):
ans = max (ans, arr[i][ 'deadline' ])
return ans
def printjobscheduling(arr, n):
arr = sorted (arr, key = cmp , reverse = True )
max_deadline = findmaxdeadline(arr, n)
ds = DisjointSet(max_deadline)
for i in range (n):
available_slot = ds.find(arr[i][ 'deadline' ])
if available_slot > 0 :
ds.merge(ds.find(available_slot - 1 ),
available_slot)
print (arr[i][ 'id' ], end = " " )
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)
|
Javascript
class DisjointSet {
constructor(n) {
this .parent = Array.from({ length: n + 1 }, (_, i) => i);
}
find(s) {
if (s === this .parent[s]) {
return s;
}
this .parent[s] = this .find( this .parent[s]);
return this .parent[s];
}
merge(u, v) {
this .parent[v] = u;
}
}
function cmp(a, b) {
return b.profit - a.profit;
}
function findmaxdeadline(arr, n) {
let ans = -Infinity;
for (let i = 0; i < n; i++) {
ans = Math.max(ans, arr[i].deadline);
}
return ans;
}
function printjobscheduling(arr, n) {
arr.sort(cmp);
const max_deadline = findmaxdeadline(arr, n);
const ds = new DisjointSet(max_deadline);
for (let i = 0; i < n; i++) {
const available_slot = ds.find(arr[i].deadline);
if (available_slot > 0) {
ds.merge(ds.find(available_slot - 1), available_slot);
console.log(arr[i].id + ' ' );
}
}
}
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);
|
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.
If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above
Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!
Last Updated :
15 Feb, 2023
Like Article
Save Article