Job Sequencing Problem
MediumUpdated: Oct 12, 2025
Problem
You are given a set of n jobs, given as arrays:
deadline[]: An array wheredeadline[i]represents the deadline of jobiand deadline (di ≥ 1),profit[]: An array whereprofit[i]represents the profit for completing jobiand profitpi(pi ≥ 0).
Only one job can be scheduled at a time, and each job takes exactly 1 unit of time. You earn profit for a job only if you complete it before or on its deadline. The task is to find the subset of jobs that maximises the total profit while adhering to deadlines.
Examples
Example 1
Input:
deadline = [4, 1, 1, 1]
profit = [20, 10, 40, 30]
Output:
Maximum profit sequence of jobs: [2, 0]
Explanation:
- Job 2 (profit = 40, deadline = 1) is scheduled first (time = 1).
- Job 0 (profit = 20, deadline = 4) is scheduled next (time = 2).
- This results in a total profit of 40 + 20 = 60.
Example 2
Input:
deadline = [2, 1, 2, 1, 3]
profit = [100, 19, 27, 25, 15]
Output:
Maximum profit sequence of jobs: [0, 2, 4]
Explanation:
- Job 0 (profit = 100, deadline = 2) is scheduled first (time = 1).
- Job 2 (profit = 27, deadline = 2) is scheduled next (time = 2).
- Job 4 (profit = 15, deadline = 3) is scheduled last (time = 3).
- This results in a total profit of 100 + 27 + 15 = 142.
Solution
Method 1 - Sorting + Greedy
Reasoning/intuition
- To maximise profit, prioritise jobs with higher profits. Hence, sort the jobs in descending order of profit.
- Use a greedy approach to schedule the highest-profit jobs first, checking the latest available timeslots (near to their deadlines).
- Keep track of which timeslots are already booked.
Algorithm
- Combine the
deadlineandprofitarrays into a single list of tuples(profit, deadline, index)for easier handling. - Sort this list by profit in descending order.
- Find the maximum deadline to create a scheduling array.
- Use slots (timing) for job scheduling, where you choose the latest available slot for each job and maximise profits greedily.
Code
CPP
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> jobScheduling(vector<int>& deadlines, vector<int>& profits) {
int n = deadlines.size();
vector<tuple<int, int, int>> jobs; // {profit, deadline, index}
for (int i = 0; i < n; i++) {
jobs.push_back({profits[i], deadlines[i], i});
}
// Sort jobs by profit in descending order
sort(jobs.rbegin(), jobs.rend());
int maxDeadline = *max_element(deadlines.begin(), deadlines.end());
vector<int> slots(maxDeadline + 1, -1); // Slot availability
vector<int> result; // Job indices to return
// Schedule jobs
for (auto& [profit, deadline, index] : jobs) {
for (int t = min(maxDeadline, deadline); t > 0; t--) {
if (slots[t] == -1) { // Free slot found
slots[t] = index;
result.push_back(index);
break;
}
}
}
return result;
}
};
int main() {
vector<int> deadlines = {4, 1, 1, 1};
vector<int> profits = {20, 10, 40, 30};
Solution s;
vector<int> result = s.jobScheduling(deadlines, profits);
cout << "Maximum profit sequence of jobs: ";
for (auto& id : result) cout << id << " ";
cout << endl;
return 0;
}
Go
package main
import (
"fmt"
"sort"
)
type Job struct {
Profit int
Deadline int
Index int
}
func jobScheduling(deadlines []int, profits []int) []int {
n := len(deadlines)
jobs := make([]Job, n)
// Combine profits and deadlines into a slice of Jobs
for i := 0; i < n; i++ {
jobs[i] = Job{Profit: profits[i], Deadline: deadlines[i], Index: i}
}
// Sort jobs by descending profit
sort.Slice(jobs, func(i, j int) bool {
return jobs[i].Profit > jobs[j].Profit
})
maxDeadline := 0
for _, job := range deadlines {
if job > maxDeadline {
maxDeadline = job
}
}
slots := make([]int, maxDeadline+1)
for i := range slots {
slots[i] = -1
}
result := []int{}
// Schedule jobs
for _, job := range jobs {
for t := min(maxDeadline, job.Deadline); t > 0; t-- {
if slots[t] == -1 { // Free slot found
slots[t] = job.Index
result = append(result, job.Index)
break
}
}
}
return result
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
deadlines := []int{4, 1, 1, 1}
profits := []int{20, 10, 40, 30}
fmt.Println("Maximum profit sequence of jobs:", jobScheduling(deadlines, profits))
}
Java
class Solution {
public List<Integer> jobScheduling(int[] deadlines, int[] profits) {
int n = deadlines.length;
List<int[]> jobs = new ArrayList<>(); // {profit, deadline, index}
for (int i = 0; i < n; i++) {
jobs.add(new int[]{profits[i], deadlines[i], i});
}
// Sort jobs by profit in descending order
Collections.sort(jobs, (a, b) -> b[0] - a[0]);
int maxDeadline = Arrays.stream(deadlines).max().getAsInt();
boolean[] slots = new boolean[maxDeadline + 1];
List<Integer> result = new ArrayList<>();
for (int[] job : jobs) {
int profit = job[0], deadline = job[1], index = job[2];
for (int t = Math.min(deadline, maxDeadline); t > 0; t--) {
if (!slots[t]) { // Free slot found
slots[t] = true;
result.add(index);
break;
}
}
}
return result;
}
public static void main(String[] args) {
int[] deadlines = {4, 1, 1, 1};
int[] profits = {20, 10, 40, 30};
Solution s = new Solution();
System.out.println("Maximum profit sequence of jobs: " + s.jobScheduling(deadlines, profits));
}
}
Kotlin
class Solution {
fun jobScheduling(deadlines: IntArray, profits: IntArray): List<Int> {
val n = deadlines.size
val jobs = mutableListOf<Job>()
// Combine profits and deadlines into a list of Jobs
for (i in 0 until n) {
jobs.add(Job(profit = profits[i], deadline = deadlines[i], index = i))
}
// Sort jobs by descending profit
jobs.sortByDescending { it.profit }
val maxDeadline = deadlines.maxOrNull() ?: 0
val slots = IntArray(maxDeadline + 1) { -1 } // Slot availability
val result = mutableListOf<Int>() // Result job indices
// Schedule jobs
for (job in jobs) {
for (t in minOf(maxDeadline, job.deadline) downTo 1) {
if (slots[t] == -1) { // Free slot found
slots[t] = job.index
result.add(job.index)
break
}
}
}
return result
}
}
fun main() {
val deadlines = intArrayOf(4, 1, 1, 1)
val profits = intArrayOf(20, 10, 40, 30)
val solution = Solution()
println("Maximum profit sequence of jobs: ${solution.jobScheduling(deadlines, profits)}")
}
Python
class Solution:
def jobScheduling(self, deadlines, profits):
# Combine job information into one list: (profit, deadline, index)
jobs = [(profits[i], deadlines[i], i) for i in range(len(profits))]
# Sort jobs by descending profit
jobs.sort(reverse=True, key=lambda x: x[0])
max_deadline = max(deadlines)
slots = [-1] * (max_deadline + 1)
result = []
for profit, deadline, index in jobs:
for t in range(min(deadline, max_deadline), 0, -1):
if slots[t] == -1: # Free slot found
slots[t] = index
result.append(index)
break
return result
# Example
deadlines = [4, 1, 1, 1]
profits = [20, 10, 40, 30]
s = Solution()
print("Maximum profit sequence of jobs:", s.jobScheduling(deadlines, profits))
Rust
use std::cmp;
#[derive(Debug)]
struct Job {
profit: i32,
deadline: usize,
index: usize,
}
struct Solution;
impl Solution {
pub fn job_scheduling(deadlines: Vec<usize>, profits: Vec<i32>) -> Vec<usize> {
let n = deadlines.len();
let mut jobs: Vec<Job> = Vec::new();
// Combine profits and deadlines into a list of Jobs
for i in 0..n {
jobs.push(Job {
profit: profits[i],
deadline: deadlines[i],
index: i,
});
}
// Sort jobs by descending profit
jobs.sort_by(|a, b| b.profit.cmp(&a.profit));
let max_deadline = deadlines.iter().copied().max().unwrap_or(0);
let mut slots = vec![-1; max_deadline + 1]; // Slot availability
let mut result = Vec::new(); // Result job indices
// Schedule jobs
for job in jobs {
for t in (1..=cmp::min(max_deadline, job.deadline)).rev() {
if slots[t] == -1 {
slots[t] = job.index as i32;
result.push(job.index);
break;
}
}
}
result
}
}
fn main() {
let deadlines = vec![4, 1, 1, 1];
let profits = vec![20, 10, 40, 30];
let result = Solution::job_scheduling(deadlines, profits);
println!("Maximum profit sequence of jobs: {:?}", result);
}
Complexity
- ⏰ Time complexity:
O(n log n + n * d)fornjobs and max deadline asd- Sorting the jobs:
O(n * log(n)). - For each job, checking available slots:
O(n * d)wheredis the maximum deadline. - Overall:
O(n * log(n) + n * d).
- Sorting the jobs:
- 🧺 Space complexity:
O(n + d). For storing the job list (input) and scheduling array:O(n + d).
Method 2 - Using disjoint set
Reasoning
- Since each job has a fixed deadline, we aim to execute it before or at its deadline. Once a time slot
tis used, we need to find the next available free time slott-1for scheduling. - Using disjoint sets, we can dynamically and efficiently track the parent (next available slot) for the time slots.
Steps
-
Sort jobs by profit:
- Arrange all jobs in descending order of profit. This ensures we always prioritise the jobs with the highest profit while scheduling.
-
Find the maximum deadline
d_max:- Determine the maximum deadline of any job. This allows us to restrict our search space for available time slots to the range
[1, d_max].
- Determine the maximum deadline of any job. This allows us to restrict our search space for available time slots to the range
-
Create disjoint sets:
- Initialise a disjoint set array, where each index represents a time slot, and its value points to the next available slot. Initially:
parent[i] = i (each slot points to itself initially)
- Initialise a disjoint set array, where each index represents a time slot, and its value points to the next available slot. Initially:
-
Process each job:
- Iterate through the sorted jobs (highest-profit first). For each job:
- Find the maximum available time slot ≤ the job's deadline using the Find operation of the disjoint set.
- If a slot is available:
- Schedule the job in that slot.
- Update the parent of the current slot to point to its previous slot (
t-1) using the Union operation. This ensures the next time slot for future jobs is correctly tracked.
- Iterate through the sorted jobs (highest-profit first). For each job:
Key Intuition of Disjoint Set Operations
-
Find operation:
- For a given slot, the Find operation helps locate the farthest available time up to that slot (e.g., if slot
5is occupied, it points to4). This prevents unnecessary linear scans for available times.
- For a given slot, the Find operation helps locate the farthest available time up to that slot (e.g., if slot
-
Union operation:
- When a slot
tis occupied, we "union" it witht-1to specify thattis no longer independent and refers to the next available slot (t-1).
- When a slot
Code
C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// Find operation for Disjoint Set with path compression
int find(vector<int>& parent, int x) {
if (parent[x] == x) {
return x;
}
return parent[x] = find(parent, parent[x]); // Path compression
}
// Union operation for Disjoint Set
void unionSet(vector<int>& parent, int x, int y) {
parent[x] = y; // Merge x into y
}
vector<int> jobScheduling(vector<int>& deadlines, vector<int>& profits) {
int n = deadlines.size();
vector<tuple<int, int, int>> jobs; // {profit, deadline, index}
// Step 1: Combine job information into one list
for (int i = 0; i < n; i++) {
jobs.push_back({profits[i], deadlines[i], i});
}
// Step 2: Sort jobs by descending profit
sort(jobs.rbegin(), jobs.rend());
// Step 3: Find the maximum deadline
int maxDeadline = *max_element(deadlines.begin(), deadlines.end());
// Step 4: Initialize disjoint set
vector<int> parent(maxDeadline + 1);
for (int i = 0; i <= maxDeadline; i++) {
parent[i] = i; // Each slot points to itself initially
}
// Step 5: Process each job
vector<int> result;
for (auto& [profit, deadline, index] : jobs) {
// Find the latest available time slot (≤ deadline)
int availableSlot = find(parent, deadline);
if (availableSlot > 0) { // If slot is available
result.push_back(index); // Schedule job
unionSet(parent, availableSlot, find(parent, availableSlot - 1)); // Merge the slot
}
}
return result;
}
};
int main() {
vector<int> deadlines = {4, 1, 1, 1};
vector<int> profits = {20, 10, 40, 30};
Solution s;
vector<int> result = s.jobScheduling(deadlines, profits);
cout << "Maximum profit sequence of jobs: ";
for (auto id : result) {
cout << id << " ";
}
cout << endl;
return 0;
}
Java
class Solution {
// Find operation for Disjoint Set
private int find(int[] parent, int x) {
if (parent[x] == x) {
return x;
}
return parent[x] = find(parent, parent[x]); // Path compression
}
// Union operation for Disjoint Set
private void union(int[] parent, int x, int y) {
parent[x] = y; // Union (link x to y)
}
public List<Integer> jobScheduling(int[] deadlines, int[] profits) {
int n = deadlines.length;
// Combine job information in (profit, deadline, index) format
List<int[]> jobs = new ArrayList<>();
for (int i = 0; i < n; i++) {
jobs.add(new int[]{profits[i], deadlines[i], i});
}
// Step 1: Sort jobs by descending profit
jobs.sort((a, b) -> b[0] - a[0]);
// Step 2: Find the maximum deadline
int maxDeadline = Arrays.stream(deadlines).max().getAsInt();
// Step 3: Initialize disjoint set
int[] parent = new int[maxDeadline + 1];
for (int i = 0; i <= maxDeadline; i++) {
parent[i] = i; // Each slot points to itself initially
}
// Step 4: Process each job
List<Integer> result = new ArrayList<>();
for (int[] job : jobs) {
int profit = job[0], deadline = job[1], index = job[2];
// Find the latest available time slot (≤ deadline)
int availableSlot = find(parent, deadline);
if (availableSlot > 0) { // If slot is available
result.add(index); // Schedule job
union(parent, availableSlot, find(parent, availableSlot - 1)); // Merge the slot
}
}
return result;
}
public static void main(String[] args) {
int[] deadlines = {4, 1, 1, 1};
int[] profits = {20, 10, 40, 30};
Solution s = new Solution();
System.out.println("Maximum profit sequence of jobs: " + s.jobScheduling(deadlines, profits));
}
}
Python
class Solution:
def find(self, parent, x):
# Find operation with path compression
if parent[x] != x:
parent[x] = self.find(parent, parent[x])
return parent[x]
def union(self, parent, x, y):
# Union operation
parent[x] = y
def jobScheduling(self, deadlines, profits):
n = len(deadlines)
# Combine job information as (profit, deadline, index)
jobs = [(profits[i], deadlines[i], i) for i in range(n)]
# Step 1: Sort jobs by descending profit
jobs.sort(reverse=True, key=lambda x: x[0])
# Step 2: Find the maximum deadline
max_deadline = max(deadlines)
# Step 3: Initialize disjoint set
parent = [i for i in range(max_deadline + 1)]
# Step 4: Process each job
result = []
for profit, deadline, index in jobs:
# Find the latest available time slot (≤ deadline)
available_slot = self.find(parent, deadline)
if available_slot > 0: # If slot is available
result.append(index) # Schedule job
self.union(parent, available_slot, self.find(parent, available_slot - 1)) # Merge the slot
return result
# Example usage
deadlines = [4, 1, 1, 1]
profits = [20, 10, 40, 30]
s = Solution()
print("Maximum profit sequence of jobs:", s.jobScheduling(deadlines, profits))
Complexity
- ⏰ Time complexity:
O(n log n)- Sorting the jobs by profit (descending) takes
O(n * log(n)). - Disjoint Set Operations:
- The
FindandUnionoperations both have an amortised time complexity ofO(α(n)), whereα(n)is the inverse Ackermann function, which grows very slowly (effectively constant for most practical purposes). - Each job involves one Find and one Union operation. For
njobs, this amounts toO(n)disjoint set operations in total.
- The
- Sorting the jobs by profit (descending) takes
- 🧺 Space complexity:
O(n + d_max)- Disjoint set array for
d_maxtime slots:O(d_max). - Storage for jobs:
O(n).
- Disjoint set array for