Weighted Job Scheduling

23 Mar, 2022
Given N jobs where every job is represented by following three elements of it.
Start TimeFinish TimeProfit or Value Associated (>= 0)
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++
Java
Python3
Javascript
// C++ program for weighted job scheduling using Naive Recursive Method#include <iostream>#include <algorithm>using namespace std; // A job has start time, finish time and profit.struct Job{    int start, finish, profit;}; // A utility function that is used for sorting events// according to finish timebool jobComparator(Job s1, Job s2){    return (s1.finish < s2.finish);} // Find the latest job (in sorted array) that doesn't// conflict with the job[i]. If there is no compatible job,// then it returns -1.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;} // A recursive function that returns the maximum possible// profit from given array of jobs.  The array of jobs must// be sorted according to finish time.int findMaxProfitRec(Job arr[], int n){    // Base case    if (n == 1) return arr[n-1].profit;     // Find profit when current job is included    int inclProf = arr[n-1].profit;    int i = latestNonConflict(arr, n);    if (i != -1)      inclProf += findMaxProfitRec(arr, i+1);     // Find profit when current job is excluded    int exclProf = findMaxProfitRec(arr, n-1);     return max(inclProf,  exclProf);} // The main function that returns the maximum possible// profit from given array of jobsint findMaxProfit(Job arr[], int n){    // Sort jobs according to finish time    sort(arr, arr+n, jobComparator);     return findMaxProfitRec(arr, n);} // Driver programint 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 program for weighted job scheduling using Naive Recursive Methodimport java.util.*;class GFG{   // A job has start time, finish time and profit.static class Job{    int start, finish, profit;    Job(int start, int finish, int profit)     {        this.start = start;        this.finish = finish;        this.profit = profit;     }} // Find the latest job (in sorted array) that doesn't// conflict with the job[i]. If there is no compatible job,// then it returns -1.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;} // A recursive function that returns the maximum possible// profit from given array of jobs. The array of jobs must// be sorted according to finish time.static int findMaxProfitRec(Job arr[], int n){    // Base case    if (n == 1) return arr[n-1].profit;     // Find profit when current job is included    int inclProf = arr[n-1].profit;    int i = latestNonConflict(arr, n);    if (i != -1)    inclProf += findMaxProfitRec(arr, i+1);     // Find profit when current job is excluded    int exclProf = findMaxProfitRec(arr, n-1);     return Math.max(inclProf, exclProf);} // The main function that returns the maximum possible// profit from given array of jobsstatic int findMaxProfit(Job arr[], int n){    // Sort jobs according to finish time    Arrays.sort(arr,new Comparator<Job>(){       public int compare(Job j1,Job j2)        {           return j1.finish-j2.finish;        }       });     return findMaxProfitRec(arr, n);} // Driver programpublic 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));}} // This code is contributed by Debojyoti Mandal
# Python3 program for weighted job scheduling using# Naive Recursive Method # Importing the following module to sort array# based on our custom comparison functionfrom functools import cmp_to_key # A job has start time, finish time and profitclass Job:         def __init__(self, start, finish, profit):                 self.start = start        self.finish = finish        self.profit = profit # A utility function that is used for# sorting events according to finish timedef jobComparator(s1, s2):         return s1.finish < s2.finish # Find the latest job (in sorted array) that# doesn't conflict with the job[i]. If there# is no compatible job, then it returns -1def latestNonConflict(arr, i):         for j in range(i - 1, -1, -1):        if arr[j].finish <= arr[i - 1].start:            return j                 return -1 # A recursive function that returns the# maximum possible profit from given# array of jobs. The array of jobs must# be sorted according to finish timedef findMaxProfitRec(arr, n):         # Base case    if n == 1:        return arr[n - 1].profit     # Find profit when current job is included    inclProf = arr[n - 1].profit    i = latestNonConflict(arr, n)         if i != -1:        inclProf += findMaxProfitRec(arr, i + 1)     # Find profit when current job is excluded    exclProf = findMaxProfitRec(arr, n - 1)    return max(inclProf, exclProf) # The main function that returns the maximum# possible profit from given array of jobsdef findMaxProfit(arr, n):         # Sort jobs according to finish time    arr = sorted(arr, key = cmp_to_key(jobComparator))    return findMaxProfitRec(arr, n) # Driver codevalues = [ (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)) # This code is code contributed by Kevin Joshi
<script> // JavaScript program for weighted job scheduling using// Naive Recursive Method // A job has start time, finish time and profitclass Job{    constructor(start, finish, profit)    {        this.start = start        this.finish = finish        this.profit = profit    }} // A utility function that is used for// sorting events according to finish timefunction jobComparator(s1, s2){         return s2.finish - s1.finish;} // Find the latest job (in sorted array) that// doesn't conflict with the job[i]. If there// is no compatible job, then it returns -1function latestNonConflict(arr, i){         for(let j = i - 1; j >= 0; j--)    {        if(arr[j].finish <= arr[i - 1].start)            return j    }           return -1} // A recursive function that returns the// maximum possible profit from given// array of jobs. The array of jobs must// be sorted according to finish timefunction findMaxProfitRec(arr, n){         // Base case    if(n == 1)        return arr[n - 1].profit     // Find profit when current job is included    let inclProf = arr[n - 1].profit    let i = latestNonConflict(arr, n)         if(i != -1)        inclProf += findMaxProfitRec(arr, i + 1)     // Find profit when current job is excluded    let exclProf = findMaxProfitRec(arr, n - 1)    return Math.max(inclProf, exclProf)} // The main function that returns the maximum// possible profit from given array of jobsfunction findMaxProfit(arr, n){         // Sort jobs according to finish time    arr.sort(jobComparator)    return findMaxProfitRec(arr, n) } // Driver codelet 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.lengthdocument.write("The optimal profit is ", findMaxProfit(arr, n),"</br>") // This code is code contributed by shinjanpatra </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++
Java
Python3
Javascript
// C++ program for weighted job scheduling using Dynamic// Programming.#include <algorithm>#include <iostream>using namespace std; // A job has start time, finish time and profit.struct Job {    int start, finish, profit;}; // A utility function that is used for sorting events// according to finish timebool jobComparator(Job s1, Job s2){    return (s1.finish < s2.finish);} // Find the latest job (in sorted array) that doesn't// conflict with the job[i]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;} // The main function that returns the maximum possible// profit from given array of jobsint findMaxProfit(Job arr[], int n){    // Sort jobs according to finish time    sort(arr, arr + n, jobComparator);     // Create an array to store solutions of subproblems.    // table[i] stores the profit for jobs till arr[i]    // (including arr[i])    int* table = new int[n];    table[0] = arr[0].profit;     // Fill entries in M[] using recursive property    for (int i = 1; i < n; i++) {        // Find profit including the current job        int inclProf = arr[i].profit;        int l = latestNonConflict(arr, i);        if (l != -1)            inclProf += table[l];         // Store maximum of including and excluding        table[i] = max(inclProf, table[i - 1]);    }     // Store result and free dynamic memory allocated for    // table[]    int result = table[n - 1];    delete[] table;     return result;} // Driver programint 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 program for weighted job scheduling using Naive// Recursive Methodimport java.util.*;class GFG {     // A job has start time, finish time and profit.    static class Job {        int start, finish, profit;        Job(int start, int finish, int profit)        {            this.start = start;            this.finish = finish;            this.profit = profit;        }    }     // Find the latest job (in sorted array) that doesn't    // conflict with the job[i]. If there is no compatible    // job, then it returns -1.    static int latestNonConflict(Job arr[], int i)    {        for (int j = i - 1; j >= 0; j--) {            // finish before next is started            if (arr[j].finish <= arr[i - 1].start)                return j;        }        return -1;    }     static int findMaxProfitDP(Job arr[], int n)    {         // Create an array to store solutions of        // subproblems.  table[i] stores the profit for jobs        // till arr[i] (including arr[i])        int[] table = new int[n];        table[0] = arr[0].profit;         // Fill entries in M[] using recursive property        for (int i = 1; i < n; i++) {            // Find profit including the current job            int inclProf = arr[i].profit;            int l = latestNonConflict(arr, i);            if (l != -1)                inclProf += table[l];             // Store maximum of including and excluding            table[i] = Math.max(inclProf, table[i - 1]);        }         // Store result and free dynamic memory allocated        // for table[]        int result = table[n - 1];         return result;    }     // The main function that returns the maximum possible    // profit from given array of jobs    static int findMaxProfit(Job arr[], int n)    {        // Sort jobs according to finish time        Arrays.sort(arr, new Comparator<Job>() {            public int compare(Job j1, Job j2)            {                return j1.finish - j2.finish;            }        });         return findMaxProfitDP(arr, n);    }     // Driver program    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 program for weighted job scheduling# using Dynamic Programming # Importing the following module to sort array# based on our custom comparison functionfrom functools import cmp_to_key # A job has start time, finish time and profit  class Job:     def __init__(self, start, finish, profit):         self.start = start        self.finish = finish        self.profit = profit # A utility function that is used for sorting# events according to finish time  def jobComparator(s1, s2):     return s1.finish < s2.finish # Find the latest job (in sorted array) that# doesn't conflict with the job[i]. If there# is no compatible job, then it returns -1  def latestNonConflict(arr, i):     for j in range(i - 1, -1, -1):        if arr[j].finish <= arr[i - 1].start:            return j     return -1 # The main function that returns the maximum possible# profit from given array of jobs  def findMaxProfit(arr, n):     # Sort jobs according to finish time    arr = sorted(arr, key=cmp_to_key(jobComparator))     # Create an array to store solutions of subproblems.    # table[i] stores the profit for jobs till arr[i]    # (including arr[i])    table = [None] * n    table[0] = arr[0].profit     # Fill entries in M[] using recursive property    for i in range(1, n):         # Find profit including the current job        inclProf = arr[i].profit        l = latestNonConflict(arr, i)         if l != -1:            inclProf += table[l]         # Store maximum of including and excluding        table[i] = max(inclProf, table[i - 1])     # Store result and free dynamic memory    # allocated for table[]    result = table[n - 1]     return result  # Driver codevalues = [(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)) # This code is contributed by Kevin Joshi
<script> // JavaScript program for weighted job scheduling using// Naive Recursive Method // A job has start time, finish time and profitclass Job{         constructor(start, finish, profit){                 this.start = start        this.finish = finish        this.profit = profit    } } // A utility function that is used for// sorting events according to finish timefunction jobComparator(s1, s2){         return s1.finish - s2.finish} // Find the latest job (in sorted array) that// doesn't conflict with the job[i]. If there// is no compatible job, then it returns -1function latestNonConflict(arr, i){         for(let j=i-1;j>=0;j--){        if (arr[j].finish <= arr[i - 1].start)            return j    }                 return -1}  // The main function that returns the maximum// possible profit from given array of jobsfunction findMaxProfit(arr, n){         // Sort jobs according to finish time    arr.sort(jobComparator)     // Create an array to store solutions of subproblems.    // table[i] stores the profit for jobs till arr[i]    // (including arr[i])    let table = new Array(n).fill(null)    table[0] = arr[0].profit     // Fill entries in M[] using recursive property    for(let i=1;i<n;i++){         // Find profit including the current job        let inclProf = arr[i].profit        let l = latestNonConflict(arr, i)         if (l != -1)            inclProf += table[l]         // Store maximum of including and excluding        table[i] = Math.max(inclProf, table[i - 1])         }     // Store result and free dynamic memory    // allocated for table[]    let result = table[n - 1]     return result} // Driver codelet 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>") // This code is code contributed by shinjanpatra </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
This article is contributed by Shivam. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above 
adrien
rayhuangcj
kevinjoshi46b
jyoti369
arorakashish0911
mrswapnilgupta
shinjanpatra
Dynamic Programming
Dynamic Programming
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Largest Sum Contiguous Subarray
Program for Fibonacci numbers
Find if there is a path between two vertices in an undirected graph
Longest Increasing Subsequence | DP-3
Longest Palindromic Substring | Set 1
Bellman–Ford Algorithm | DP-23
Sieve of Eratosthenes
Find minimum number of coins that make a given value
Minimum number of jumps to reach end
Count number of binary strings without consecutive 1's