Given N jobs where every job is represented by following three elements of it.
- Start Time
- Finish Time
- Profit or Value Associated (>= 0)
Find the maximum profit subset of jobs such that no two jobs in the subset overlap.
Example:
Input: Number of Jobs n = 4
Job Details {Start Time, Finish Time, Profit}
Job 1: {1, 2, 50}
Job 2: {3, 5, 20}
Job 3: {6, 19, 100}
Job 4: {2, 100, 200}
Output: The maximum profit is 250.
We can get the maximum profit by scheduling jobs 1 and 4.
Note that there is longer schedules possible Jobs 1, 2 and 3
but the profit with this schedule is 20+50+100 which is less than 250.
A simple version of this problem is discussed here where every job has the same profit or value. The Greedy Strategy for activity selection doesn’t work here as a schedule with more jobs may have smaller profit or value.
The above problem can be solved using the following recursive solution.
1) First sort jobs according to finish time.
2) Now apply following recursive process.
// Here arr[] is array of n jobs
findMaximumProfit(arr[], n)
{
a) if (n == 1) return arr[0];
b) Return the maximum of following two profits.
(i) Maximum profit by excluding current job, i.e.,
findMaximumProfit(arr, n-1)
(ii) Maximum profit by including the current job
}
How to find the profit including current job?
The idea is to find the latest job before the current job (in
sorted array) that doesn't conflict with current job 'arr[n-1]'.
Once we find such a job, we recur for all jobs till that job and
add profit of current job to result.
In the above example, "job 1" is the latest non-conflicting
for "job 4" and "job 2" is the latest non-conflicting for "job 3".
The following is the implementation of the above naive recursive method.
C++
#include <iostream>
#include <algorithm>
using namespace std;
struct Job
{
int start, finish, profit;
};
bool jobComparator(Job s1, Job s2)
{
return (s1.finish < s2.finish);
}
int latestNonConflict(Job arr[], int i)
{
for ( int j=i-1; j>=0; j--)
{
if (arr[j].finish <= arr[i-1].start)
return j;
}
return -1;
}
int findMaxProfitRec(Job arr[], int n)
{
if (n == 1) return arr[n-1].profit;
int inclProf = arr[n-1].profit;
int i = latestNonConflict(arr, n);
if (i != -1)
inclProf += findMaxProfitRec(arr, i+1);
int exclProf = findMaxProfitRec(arr, n-1);
return max(inclProf, exclProf);
}
int findMaxProfit(Job arr[], int n)
{
sort(arr, arr+n, jobComparator);
return findMaxProfitRec(arr, n);
}
int main()
{
Job arr[] = {{3, 10, 20}, {1, 2, 50}, {6, 19, 100}, {2, 100, 200}};
int n = sizeof (arr)/ sizeof (arr[0]);
cout << "The optimal profit is " << findMaxProfit(arr, n);
return 0;
}
|
Java
import java.util.*;
class GFG
{
static class Job
{
int start, finish, profit;
Job( int start, int finish, int profit)
{
this .start = start;
this .finish = finish;
this .profit = profit;
}
}
static int latestNonConflict(Job arr[], int i)
{
for ( int j = i - 1 ; j >= 0 ; j--)
{
if (arr[j].finish <= arr[i - 1 ].start)
return j;
}
return - 1 ;
}
static int findMaxProfitRec(Job arr[], int n)
{
if (n == 1 ) return arr[n- 1 ].profit;
int inclProf = arr[n- 1 ].profit;
int i = latestNonConflict(arr, n);
if (i != - 1 )
inclProf += findMaxProfitRec(arr, i+ 1 );
int exclProf = findMaxProfitRec(arr, n- 1 );
return Math.max(inclProf, exclProf);
}
static int findMaxProfit(Job arr[], int n)
{
Arrays.sort(arr, new Comparator<Job>(){
public int compare(Job j1,Job j2)
{
return j1.finish-j2.finish;
}
});
return findMaxProfitRec(arr, n);
}
public static void main(String args[])
{
int m = 4 ;
Job arr[] = new Job[m];
arr[ 0 ] = new Job( 3 , 10 , 20 );
arr[ 1 ] = new Job( 1 , 2 , 50 );
arr[ 2 ] = new Job( 6 , 19 , 100 );
arr[ 3 ] = new Job( 2 , 100 , 200 );
int n =arr.length;
System.out.println( "The optimal profit is " + findMaxProfit(arr, n));
}
}
|
Python3
from functools import cmp_to_key
class Job:
def __init__( self , start, finish, profit):
self .start = start
self .finish = finish
self .profit = profit
def jobComparator(s1, s2):
return s1.finish < s2.finish
def latestNonConflict(arr, i):
for j in range (i - 1 , - 1 , - 1 ):
if arr[j].finish < = arr[i - 1 ].start:
return j
return - 1
def findMaxProfitRec(arr, n):
if n = = 1 :
return arr[n - 1 ].profit
inclProf = arr[n - 1 ].profit
i = latestNonConflict(arr, n)
if i ! = - 1 :
inclProf + = findMaxProfitRec(arr, i + 1 )
exclProf = findMaxProfitRec(arr, n - 1 )
return max (inclProf, exclProf)
def findMaxProfit(arr, n):
arr = sorted (arr, key = cmp_to_key(jobComparator))
return findMaxProfitRec(arr, n)
values = [ ( 3 , 10 , 20 ), ( 1 , 2 , 50 ),
( 6 , 19 , 100 ), ( 2 , 100 , 200 ) ]
arr = []
for i in values:
arr.append(Job(i[ 0 ], i[ 1 ], i[ 2 ]))
n = len (arr)
print ( "The optimal profit is" , findMaxProfit(arr, n))
|
Javascript
<script>
class Job
{
constructor(start, finish, profit)
{
this .start = start
this .finish = finish
this .profit = profit
}
}
function jobComparator(s1, s2){
return s2.finish - s1.finish;
}
function latestNonConflict(arr, i){
for (let j = i - 1; j >= 0; j--)
{
if (arr[j].finish <= arr[i - 1].start)
return j
}
return -1
}
function findMaxProfitRec(arr, n){
if (n == 1)
return arr[n - 1].profit
let inclProf = arr[n - 1].profit
let i = latestNonConflict(arr, n)
if (i != -1)
inclProf += findMaxProfitRec(arr, i + 1)
let exclProf = findMaxProfitRec(arr, n - 1)
return Math.max(inclProf, exclProf)
}
function findMaxProfit(arr, n){
arr.sort(jobComparator)
return findMaxProfitRec(arr, n)
}
let values = [ [3, 10, 20], [1, 2, 50],
[6, 19, 100], [2, 100, 200] ]
let arr = []
for (let i of values)
arr.push( new Job(i[0], i[1], i[2]))
let n = arr.length
document.write( "The optimal profit is " , findMaxProfit(arr, n), "</br>" )
</script>
|
Output:
The optimal profit is 250
The above solution may contain many overlapping subproblems. For example, if lastNonConflicting() always returns the previous job, then findMaxProfitRec(arr, n-1) is called twice and the time complexity becomes O(n*2n). As another example when lastNonConflicting() returns previous to the previous job, there are two recursive calls, for n-2 and n-1. In this example case, recursion becomes the same as Fibonacci Numbers.
So this problem has both properties of Dynamic Programming, Optimal Substructure, and Overlapping Subproblems.
Like other Dynamic Programming Problems, we can solve this problem by making a table that stores solutions of subproblems.
Below is an implementation based on Dynamic Programming.
C++
#include <algorithm>
#include <iostream>
using namespace std;
struct Job {
int start, finish, profit;
};
bool jobComparator(Job s1, Job s2)
{
return (s1.finish < s2.finish);
}
int latestNonConflict(Job arr[], int i)
{
for ( int j = i - 1; j >= 0; j--) {
if (arr[j].finish <= arr[i].start)
return j;
}
return -1;
}
int findMaxProfit(Job arr[], int n)
{
sort(arr, arr + n, jobComparator);
int * table = new int [n];
table[0] = arr[0].profit;
for ( int i = 1; i < n; i++) {
int inclProf = arr[i].profit;
int l = latestNonConflict(arr, i);
if (l != -1)
inclProf += table[l];
table[i] = max(inclProf, table[i - 1]);
}
int result = table[n - 1];
delete [] table;
return result;
}
int main()
{
Job arr[] = { { 3, 10, 20 },
{ 1, 2, 50 },
{ 6, 19, 100 },
{ 2, 100, 200 } };
int n = sizeof (arr) / sizeof (arr[0]);
cout << "The optimal profit is "
<< findMaxProfit(arr, n);
return 0;
}
|
Java
import java.util.*;
class GFG {
static class Job {
int start, finish, profit;
Job( int start, int finish, int profit)
{
this .start = start;
this .finish = finish;
this .profit = profit;
}
}
static int latestNonConflict(Job arr[], int i)
{
for ( int j = i - 1 ; j >= 0 ; j--) {
if (arr[j].finish <= arr[i - 1 ].start)
return j;
}
return - 1 ;
}
static int findMaxProfitDP(Job arr[], int n)
{
int [] table = new int [n];
table[ 0 ] = arr[ 0 ].profit;
for ( int i = 1 ; i < n; i++) {
int inclProf = arr[i].profit;
int l = latestNonConflict(arr, i);
if (l != - 1 )
inclProf += table[l];
table[i] = Math.max(inclProf, table[i - 1 ]);
}
int result = table[n - 1 ];
return result;
}
static int findMaxProfit(Job arr[], int n)
{
Arrays.sort(arr, new Comparator<Job>() {
public int compare(Job j1, Job j2)
{
return j1.finish - j2.finish;
}
});
return findMaxProfitDP(arr, n);
}
public static void main(String args[])
{
int m = 4 ;
Job arr[] = new Job[m];
arr[ 0 ] = new Job( 3 , 10 , 20 );
arr[ 1 ] = new Job( 1 , 2 , 50 );
arr[ 2 ] = new Job( 6 , 19 , 100 );
arr[ 3 ] = new Job( 2 , 100 , 200 );
int n = arr.length;
System.out.println( "The optimal profit is "
+ findMaxProfit(arr, n));
}
}
|
Python3
from functools import cmp_to_key
class Job:
def __init__( self , start, finish, profit):
self .start = start
self .finish = finish
self .profit = profit
def jobComparator(s1, s2):
return s1.finish < s2.finish
def latestNonConflict(arr, i):
for j in range (i - 1 , - 1 , - 1 ):
if arr[j].finish < = arr[i - 1 ].start:
return j
return - 1
def findMaxProfit(arr, n):
arr = sorted (arr, key = cmp_to_key(jobComparator))
table = [ None ] * n
table[ 0 ] = arr[ 0 ].profit
for i in range ( 1 , n):
inclProf = arr[i].profit
l = latestNonConflict(arr, i)
if l ! = - 1 :
inclProf + = table[l]
table[i] = max (inclProf, table[i - 1 ])
result = table[n - 1 ]
return result
values = [( 3 , 10 , 20 ), ( 1 , 2 , 50 ),
( 6 , 19 , 100 ), ( 2 , 100 , 200 )]
arr = []
for i in values:
arr.append(Job(i[ 0 ], i[ 1 ], i[ 2 ]))
n = len (arr)
print ( "The optimal profit is" , findMaxProfit(arr, n))
|
Javascript
<script>
class Job{
constructor(start, finish, profit){
this .start = start
this .finish = finish
this .profit = profit
}
}
function jobComparator(s1, s2){
return s1.finish - s2.finish
}
function latestNonConflict(arr, i){
for (let j=i-1;j>=0;j--){
if (arr[j].finish <= arr[i - 1].start)
return j
}
return -1
}
function findMaxProfit(arr, n){
arr.sort(jobComparator)
let table = new Array(n).fill( null )
table[0] = arr[0].profit
for (let i=1;i<n;i++){
let inclProf = arr[i].profit
let l = latestNonConflict(arr, i)
if (l != -1)
inclProf += table[l]
table[i] = Math.max(inclProf, table[i - 1])
}
let result = table[n - 1]
return result
}
let values = [ [3, 10, 20], [1, 2, 50], [6, 19, 100], [2, 100, 200] ]
let arr = []
for (let i of values)
arr.push( new Job(i[0], i[1], i[2]))
let n = arr.length
document.write( "The optimal profit is " , findMaxProfit(arr, n), "</br>" )
</script>
|
Output:
The optimal profit is 250
Time Complexity of the above Dynamic Programming Solution is O(n2). Note that the above solution can be optimized to O(nLogn) using Binary Search in latestNonConflict() instead of linear search. Thanks to Garvit for suggesting this optimization. Please refer below post for details.
Weighted Job Scheduling in O(n Log n) time
References:
http://courses.cs.washington.edu/courses/cse521/13wi/slides/06dp-sched.pdf
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 :
23 Mar, 2022
Like Article
Save Article