Given a 2D array jobs[][] of size n × 3, where each row represents a single job with the following details:
- jobs[i][0] → Start time of the job
- jobs[i][1] → End time of the job
- jobs[i][2] → Profit earned by completing the job
Find the maximum profit you can earn by scheduling non-overlapping jobs.
Note: Two jobs are said to be non-overlapping if the end time of one job is less than or equal to the start time of the next job. If a job ends at time X, another job can start exactly at time X.
Examples:
Input: jobs[][] = [[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]]
Output: 250
Explanation: The first and fourth jobs with the time range [1, 2] and [2, 100] can be chosen to give maximum profit of 50 + 200 = 250.
Input: jobs[][] = [[1, 3, 60],
[2, 5, 50],
[4, 6, 70],
[5, 7, 30]]
Output: 130
Explanation: The first and third jobs with the time range [1, 3] and [4, 6] can be chosen to give maximum profit of 60 + 70 = 130.
[Naive Approach - 1] Using Recursion - O(2n) Time and O(n) Space
The idea is to recursively explore all combinations of job selections to find the maximum profit.
First, sort the jobs normally by their start time. For each job index i, we have two options:
- Take the current job → Add its profit and recursively move to the next job whose start time is not less than the current job’s end time.
- Skip the current job → Move to the next job directly.
The recursion explores both possibilities for every job and returns the maximum profit achievable.
C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends
// Recursive utility function to find maximum profit
int maxProfitRec(int i, vector<vector<int>> &jobs) {
// Base case
if (i >= jobs.size()) return 0;
// Option 1: Skip the current job
int skip = maxProfitRec(i + 1, jobs);
// Option 2: Take the current job
int next = i + 1;
while (next < jobs.size() && jobs[next][0] < jobs[i][1]) next++;
int take = jobs[i][2] + maxProfitRec(next, jobs);
return max(take, skip);
}
// Function to find maximum profit
int maxProfit(vector<vector<int>> &jobs) {
sort(jobs.begin(), jobs.end());
return maxProfitRec(0, jobs);
}
//Driver Code Starts
int main() {
vector<vector<int>> jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
cout << maxProfit(jobs);
return 0;
}
//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
class GFG {
//Driver Code Ends
// Recursive utility function to find maximum profit
static int maxProfitRec(int i, int[][] jobs) {
// Base case
if (i >= jobs.length) return 0;
// Option 1: Skip the current job
int skip = maxProfitRec(i + 1, jobs);
// Option 2: Take the current job
int next = i + 1;
while (next < jobs.length && jobs[next][0] < jobs[i][1]) next++;
int take = jobs[i][2] + maxProfitRec(next, jobs);
return Math.max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
return maxProfitRec(0, jobs);
}
//Driver Code Starts
public static void main(String[] args) {
int[][] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
System.out.println(maxProfit(jobs));
}
}
//Driver Code Ends
Python
# Recursive utility function to find maximum profit
def maxProfitRec(i, jobs):
# Base case
if i >= len(jobs):
return 0
# Option 1: Skip the current job
skip = maxProfitRec(i + 1, jobs)
# Option 2: Take the current job
next = i + 1
while next < len(jobs) and jobs[next][0] < jobs[i][1]:
next += 1
take = jobs[i][2] + maxProfitRec(next, jobs)
return max(take, skip)
# Function to find maximum profit
def maxProfit(jobs):
jobs.sort()
return maxProfitRec(0, jobs)
#Driver Code Starts
if __name__ == '__main__':
jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
]
print(maxProfit(jobs))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
// Recursive utility function to find maximum profit
static int maxProfitRec(int i, int[,] jobs) {
// Base case
if (i >= jobs.GetLength(0)) return 0;
// Option 1: Skip the current job
int skip = maxProfitRec(i + 1, jobs);
// Option 2: Take the current job
int next = i + 1;
while (next < jobs.GetLength(0) && jobs[next, 0] < jobs[i, 1]) next++;
int take = jobs[i, 2] + maxProfitRec(next, jobs);
return Math.Max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[,] jobs) {
int n = jobs.GetLength(0);
List<int[]> list = new List<int[]>();
for (int i = 0; i < n; i++)
list.Add(new int[] { jobs[i, 0], jobs[i, 1], jobs[i, 2] });
list.Sort((a, b) => a[0].CompareTo(b[0]));
int[,] sorted = new int[n, 3];
for (int i = 0; i < n; i++)
for (int j = 0; j < 3; j++)
sorted[i, j] = list[i][j];
return maxProfitRec(0, sorted);
}
//Driver Code Starts
public static void Main() {
int[,] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
Console.WriteLine(maxProfit(jobs));
}
}
//Driver Code Ends
JavaScript
// Recursive utility function to find maximum profit
function maxProfitRec(i, jobs) {
// Base case
if (i >= jobs.length) return 0;
// Option 1: Skip the current job
let skip = maxProfitRec(i + 1, jobs);
// Option 2: Take the current job
let next = i + 1;
while (next < jobs.length && jobs[next][0] < jobs[i][1]) next++;
let take = jobs[i][2] + maxProfitRec(next, jobs);
return Math.max(take, skip);
}
// Function to find maximum profit
function maxProfit(jobs) {
jobs.sort((a, b) => a[0] - b[0]);
return maxProfitRec(0, jobs);
}
// Driver code
//Driver Code Starts
let jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
];
console.log(maxProfit(jobs));
//Driver Code Ends
[Naive Approach - 2] Using Recursion with Binary Search - O(2n) Time and O(n) Space
The idea is to optimize the recursive solution by finding the next non-overlapping job using binary search instead of a linear scan.
After sorting the jobs by start time, for each job:
- If we take it, we add its profit and move to the next job whose start time is ≥ current job’s end time (found using binary search).
- If we skip it, we simply move to the next job.
We return the maximum of both choices.
C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends
// Utility function to find the next non-overlapping job using binary search
int findNextJob(int i, vector<vector<int>> &jobs) {
int low = i + 1, high = jobs.size() - 1, ans = jobs.size();
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1]) {
ans = mid;
high = mid - 1;
} else
low = mid + 1;
}
return ans;
}
// Recursive utility function to find maximum profit
int maxProfitRec(int i, vector<vector<int>> &jobs) {
// Base case
if (i >= jobs.size()) return 0;
// Skip the current job
int skip = maxProfitRec(i + 1, jobs);
// Take the current job
int next = findNextJob(i, jobs);
int take = jobs[i][2] + maxProfitRec(next, jobs);
return max(take, skip);
}
// Function to find maximum profit
int maxProfit(vector<vector<int>> &jobs) {
sort(jobs.begin(), jobs.end());
return maxProfitRec(0, jobs);
}
//Driver Code Starts
int main() {
vector<vector<int>> jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
cout << maxProfit(jobs);
return 0;
}
//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
class GFG {
//Driver Code Ends
// Utility function to find the next non-overlapping job
static int findNextJob(int i, int[][] jobs) {
int low = i + 1, high = jobs.length - 1, ans = jobs.length;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1]) {
ans = mid;
high = mid - 1;
} else
low = mid + 1;
}
return ans;
}
// Recursive utility function to find maximum profit
static int maxProfitRec(int i, int[][] jobs) {
if (i >= jobs.length) return 0;
int skip = maxProfitRec(i + 1, jobs);
int next = findNextJob(i, jobs);
int take = jobs[i][2] + maxProfitRec(next, jobs);
return Math.max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
return maxProfitRec(0, jobs);
}
//Driver Code Starts
public static void main(String[] args) {
int[][] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
System.out.println(maxProfit(jobs));
}
}
//Driver Code Ends
Python
# Utility function for binary search
def findNextJob(jobs, i):
low, high = i + 1, len(jobs) - 1
while low <= high:
mid = (low + high) // 2
if jobs[mid][0] >= jobs[i][1]:
high = mid - 1
else:
low = mid + 1
return low
# Recursive utility function to find maximum profit
def maxProfitRec(i, jobs):
# Base case
if i >= len(jobs):
return 0
# Skip current job
skip = maxProfitRec(i + 1, jobs)
# Take current job
next_index = findNextJob(jobs, i)
take = jobs[i][2] + maxProfitRec(next_index, jobs)
return max(take, skip)
# Function to find maximum profit
def maxProfit(jobs):
jobs.sort()
return maxProfitRec(0, jobs)
if __name__ == '__main__':
#Driver Code Starts
jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
]
print(maxProfit(jobs))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
// Utility function for binary search
static int findNextJob(int[,] jobs, int i, int n) {
int low = i + 1, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid, 0] >= jobs[i, 1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive utility function to find maximum profit
static int maxProfitRec(int i, int[,] jobs, int n) {
// Base case
if (i >= n)
return 0;
// Skip current job
int skip = maxProfitRec(i + 1, jobs, n);
// Take current job
int nextIndex = findNextJob(jobs, i, n);
int take = jobs[i, 2] + maxProfitRec(nextIndex, jobs, n);
return Math.Max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[,] jobs) {
int n = jobs.GetLength(0);
List<int[]> list = new List<int[]>();
for (int i = 0; i < n; i++)
list.Add(new int[] { jobs[i, 0], jobs[i, 1], jobs[i, 2] });
list.Sort((a, b) => a[0].CompareTo(b[0]));
for (int i = 0; i < n; i++) {
jobs[i, 0] = list[i][0];
jobs[i, 1] = list[i][1];
jobs[i, 2] = list[i][2];
}
return maxProfitRec(0, jobs, n);
}
//Driver Code Starts
static void Main() {
int[,] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
Console.WriteLine(maxProfit(jobs));
}
}
//Driver Code Ends
JavaScript
// Utility function for binary search
function findNextJob(jobs, i) {
let low = i + 1, high = jobs.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (jobs[mid][0] >= jobs[i][1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive utility function to find maximum profit
function maxProfitRec(i, jobs) {
// Base case
if (i >= jobs.length) return 0;
// Skip current job
let skip = maxProfitRec(i + 1, jobs);
// Take current job
let next = findNextJob(jobs, i);
let take = jobs[i][2] + maxProfitRec(next, jobs);
return Math.max(take, skip);
}
// Function to find maximum profit
function maxProfit(jobs) {
jobs.sort((a, b) => a[0] - b[0]);
return maxProfitRec(0, jobs);
}
// Driver code
//Driver Code Starts
const jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
];
console.log(maxProfit(jobs));
//Driver Code Ends
[Expected Approach - 1] Using Memoization - O(n log(n)) Time and O(n) Space
The idea is to optimize the recursive solution by storing the results of overlapping subproblems.
For each job at index i, we have two choices:
- Skip the job: Move to the next index i + 1.
- Take the job: Add its profit and jump to the next non-overlapping job (found using binary search).
To avoid recomputation, we store the maximum profit from index i to the end in a 1D array dp[], where:
dp[i] = max(take, skip).
C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends
// Utility function for binary search
int findNextJob(vector<vector<int>> &jobs, int i) {
int low = i + 1, high = jobs.size() - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive function with memoization
int maxProfitRec(int i, vector<vector<int>> &jobs, vector<int> &dp) {
// Base case
if (i >= jobs.size()) return 0;
if (dp[i] != -1) return dp[i];
// Skip current job
int skip = maxProfitRec(i + 1, jobs, dp);
// Take current job
int nextIndex = findNextJob(jobs, i);
int take = jobs[i][2] + maxProfitRec(nextIndex, jobs, dp);
return dp[i] = max(take, skip);
}
// Function to find maximum profit
int maxProfit(vector<vector<int>> &jobs) {
sort(jobs.begin(), jobs.end());
int n = jobs.size();
vector<int> dp(n, -1);
return maxProfitRec(0, jobs, dp);
}
//Driver Code Starts
int main() {
vector<vector<int>> jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
cout << maxProfit(jobs);
return 0;
}
//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
class GFG {
//Driver Code Ends
// Utility function for binary search
static int findNextJob(int[][] jobs, int i) {
int low = i + 1, high = jobs.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive function with memoization
static int maxProfitRec(int i, int[][] jobs, int[] dp) {
// Base case
if (i >= jobs.length) return 0;
if (dp[i] != -1) return dp[i];
// Skip current job
int skip = maxProfitRec(i + 1, jobs, dp);
// Take current job
int nextIndex = findNextJob(jobs, i);
int take = jobs[i][2] + maxProfitRec(nextIndex, jobs, dp);
return dp[i] = Math.max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
int n = jobs.length;
int[] dp = new int[n];
Arrays.fill(dp, -1);
return maxProfitRec(0, jobs, dp);
}
//Driver Code Starts
public static void main(String[] args) {
int[][] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
System.out.println(maxProfit(jobs));
}
}
//Driver Code Ends
Python
# Utility function for binary search
def findNextJob(jobs, i):
low, high = i + 1, len(jobs) - 1
while low <= high:
mid = (low + high) // 2
if jobs[mid][0] >= jobs[i][1]:
high = mid - 1
else:
low = mid + 1
return low
# Recursive function with memoization
def maxProfitRec(i, jobs, dp):
# Base case
if i >= len(jobs):
return 0
if dp[i] != -1:
return dp[i]
# Skip current job
skip = maxProfitRec(i + 1, jobs, dp)
# Take current job
nextIndex = findNextJob(jobs, i)
take = jobs[i][2] + maxProfitRec(nextIndex, jobs, dp)
dp[i] = max(take, skip)
return dp[i]
# Function to find maximum profit
def maxProfit(jobs):
jobs.sort()
n = len(jobs)
dp = [-1] * n
return maxProfitRec(0, jobs, dp)
if __name__ == "__main__":
#Driver Code Starts
jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
]
print(maxProfit(jobs))
#Driver Code Ends
C#
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
// Utility function for binary search
static int findNextJob(int[,] jobs, int i, int n) {
int low = i + 1, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid, 0] >= jobs[i, 1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive function with memoization
static int maxProfitRec(int i, int[,] jobs, int[] dp, int n) {
// Base case
if (i >= n) return 0;
if (dp[i] != -1) return dp[i];
// Skip current job
int skip = maxProfitRec(i + 1, jobs, dp, n);
// Take current job
int nextIndex = findNextJob(jobs, i, n);
int take = jobs[i, 2] + maxProfitRec(nextIndex, jobs, dp, n);
return dp[i] = Math.Max(take, skip);
}
// Function to find maximum profit
static int maxProfit(int[,] jobs) {
int n = jobs.GetLength(0);
// Convert to jagged array for sorting
int[][] jobArr = new int[n][];
for (int i = 0; i < n; i++)
jobArr[i] = new int[] { jobs[i, 0], jobs[i, 1], jobs[i, 2] };
// Sort by start time (O(n log n))
Array.Sort(jobArr, (a, b) => a[0].CompareTo(b[0]));
// Copy back to 2D array
for (int i = 0; i < n; i++) {
jobs[i, 0] = jobArr[i][0];
jobs[i, 1] = jobArr[i][1];
jobs[i, 2] = jobArr[i][2];
}
int[] dp = new int[n];
Array.Fill(dp, -1);
return maxProfitRec(0, jobs, dp, n);
}
//Driver Code Starts
public static void Main() {
int[,] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
Console.WriteLine(maxProfit(jobs));
}
}
//Driver Code Ends
JavaScript
// Utility function for binary search
function findNextJob(jobs, i) {
let low = i + 1, high = jobs.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (jobs[mid][0] >= jobs[i][1])
high = mid - 1;
else
low = mid + 1;
}
return low;
}
// Recursive function with memoization
function maxProfitRec(i, jobs, dp) {
// Base case
if (i >= jobs.length) return 0;
if (dp[i] !== -1) return dp[i];
// Skip current job
let skip = maxProfitRec(i + 1, jobs, dp);
// Take current job
let nextIndex = findNextJob(jobs, i);
let take = jobs[i][2] + maxProfitRec(nextIndex, jobs, dp);
dp[i] = Math.max(take, skip);
return dp[i];
}
// Function to find maximum profit
function maxProfit(jobs) {
jobs.sort((a, b) => a[0] - b[0]);
let n = jobs.length;
let dp = new Array(n).fill(-1);
return maxProfitRec(0, jobs, dp);
}
// Driver Code
//Driver Code Starts
const jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
];
console.log(maxProfit(jobs));
//Driver Code Ends
[Expected Approach - 2] Using Tabulation - O(n log(n)) Time and O(n) Space
The idea is to use a bottom-up dynamic programming approach to iteratively compute the maximum profit.
We define:
dp[i] → the maximum profit that can be obtained starting from the i-th job till the last job.
We first sort the jobs by their start time. Then, we fill the dp array from right to left, since the profit for the current job depends on the future jobs.
For each job i:
- Use binary search to find the next job next whose start time is not less than the end time of job i.
- Compute two options:
take = jobs[i][2] + dp[next] (take current job and add profit from next non-overlapping job)
skip = dp[i + 1] (skip current job) - dp[i] = max(take, skip)
Finally, dp[0] gives the maximum profit obtainable from the first job.
C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends
// Utility function for binary search
int findNextJob(vector<vector<int>> &jobs, int i) {
int low = i + 1, high = jobs.size() - 1, ans = jobs.size();
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1]) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
// Function to find maximum profit using tabulation
int maxProfit(vector<vector<int>> &jobs) {
sort(jobs.begin(), jobs.end());
int n = jobs.size();
vector<int> dp(n + 1, 0);
// Build dp table from end
for (int i = n - 1; i >= 0; i--) {
int next = findNextJob(jobs, i);
int take = jobs[i][2] + dp[next];
int skip = dp[i + 1];
dp[i] = max(take, skip);
}
return dp[0];
}
//Driver Code Starts
int main() {
vector<vector<int>> jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
cout << maxProfit(jobs);
return 0;
}
//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
class GFG {
//Driver Code Ends
// Utility function for binary search
static int findNextJob(int[][] jobs, int i) {
int low = i + 1, high = jobs.length - 1, ans = jobs.length;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid][0] >= jobs[i][1]) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
// Function to find maximum profit using tabulation
static int maxProfit(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
int n = jobs.length;
int[] dp = new int[n + 1];
// Build dp table from end
for (int i = n - 1; i >= 0; i--) {
int next = findNextJob(jobs, i);
int take = jobs[i][2] + dp[next];
int skip = dp[i + 1];
dp[i] = Math.max(take, skip);
}
return dp[0];
}
//Driver Code Starts
public static void main(String[] args) {
int[][] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
System.out.println(maxProfit(jobs));
}
}
//Driver Code Ends
Python
# Utility function for binary search
def findNextJob(jobs, i):
low, high = i + 1, len(jobs) - 1
ans = len(jobs)
while low <= high:
mid = (low + high) // 2
if jobs[mid][0] >= jobs[i][1]:
ans = mid
high = mid - 1
else:
low = mid + 1
return ans
# Function to find maximum profit using tabulation
def maxProfit(jobs):
jobs.sort()
n = len(jobs)
dp = [0] * (n + 1)
# Build dp table from end
for i in range(n - 1, -1, -1):
next = findNextJob(jobs, i)
take = jobs[i][2] + dp[next]
skip = dp[i + 1]
dp[i] = max(take, skip)
return dp[0]
if __name__ == '__main__':
#Driver Code Starts
jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
]
print(maxProfit(jobs))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
// Utility function for binary search
static int findNextJob(List<(int, int, int)> jobs, int i) {
int n = jobs.Count;
int low = i + 1, high = n - 1, ans = n;
while (low <= high) {
int mid = (low + high) / 2;
if (jobs[mid].Item1 >= jobs[i].Item2)
{
ans = mid;
high = mid - 1;
}
else
{
low = mid + 1;
}
}
return ans;
}
// Function to find maximum profit using tabulation
static int maxProfit(int[,] jobs) {
int n = jobs.GetLength(0);
// Convert array to list of tuples for easy sorting
var jobList = new List<(int, int, int)>();
for (int i = 0; i < n; i++)
jobList.Add((jobs[i, 0], jobs[i, 1], jobs[i, 2]));
// Sort by start time
jobList.Sort((a, b) => a.Item1.CompareTo(b.Item1));
int[] dp = new int[n + 1];
// Build dp table from end
for (int i = n - 1; i >= 0; i--)
{
int next = findNextJob(jobList, i);
int take = jobList[i].Item3 + dp[next];
int skip = dp[i + 1];
dp[i] = Math.Max(take, skip);
}
return dp[0];
}
//Driver Code Starts
static void Main() {
int[,] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
Console.WriteLine(maxProfit(jobs));
}
}
//Driver Code Ends
JavaScript
// Utility function for binary search
function findNextJob(jobs, i) {
let low = i + 1, high = jobs.length - 1, ans = jobs.length;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (jobs[mid][0] >= jobs[i][1]) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
// Function to find maximum profit using tabulation
function maxProfit(jobs) {
jobs.sort((a, b) => a[0] - b[0]);
const n = jobs.length;
const dp = new Array(n + 1).fill(0);
// Build dp table from end
for (let i = n - 1; i >= 0; i--) {
const next = findNextJob(jobs, i);
const take = jobs[i][2] + dp[next];
const skip = dp[i + 1];
dp[i] = Math.max(take, skip);
}
return dp[0];
}
// Driver code
//Driver Code Starts
const jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
];
console.log(maxProfit(jobs));
//Driver Code Ends
[Expected Approach - 3] Using Priority Queue - O(n log(n)) Time and O(n) Space
We first sort all jobs by their start time. Then, we use a min-heap (priority queue) to keep track of jobs based on their end time.
At each step, we process jobs in increasing order of start time and remove from the heap all jobs that end before or at the current job’s start time — because they don’t overlap.
For every removed job, we update the maximum profit so far, since those jobs have already ended and can safely be combined with the current one.
Then we push the current job into the heap with the total profit = current job’s profit + best profit so far.
This works because:
- Sorting by start time ensures we always process compatible jobs in order.
- The heap efficiently keeps track of the earliest finishing jobs.
- When a job ends before the current one starts, it will also not overlap with any future job, so we can finalize its contribution to profit.
In the end, the maximum profit among all jobs in the heap is our answer.
C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
//Driver Code Ends
int maxProfit(vector<vector<int>> &jobs) {
sort(jobs.begin(), jobs.end());
// Min-heap to store {end time, total profit till now}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
int maxProfit = 0;
for (auto &job : jobs) {
int start = job[0], end = job[1], profit = job[2];
// Remove jobs that end before current job starts
while (!pq.empty() && pq.top().first <= start) {
maxProfit = max(maxProfit, pq.top().second);
pq.pop();
}
// Push current job with profit + best profit so far
pq.push({end, profit + maxProfit});
}
// Final maximum profit among all chains
while (!pq.empty()) {
maxProfit = max(maxProfit, pq.top().second);
pq.pop();
}
return maxProfit;
}
//Driver Code Starts
int main() {
vector<vector<int>> jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
cout << maxProfit(jobs);
return 0;
}
//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
import java.util.PriorityQueue;
class GFG {
//Driver Code Ends
static int maxProfit(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
// Min-heap to store {end time, total profit till now}
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
int maxProfit = 0;
for (int[] job : jobs) {
int start = job[0], end = job[1], profit = job[2];
// Remove jobs that end before current job starts
while (!pq.isEmpty() && pq.peek()[0] <= start) {
maxProfit = Math.max(maxProfit, pq.peek()[1]);
pq.poll();
}
// Push current job with profit + best profit so far
pq.offer(new int[]{end, profit + maxProfit});
}
// Final maximum profit among all chains
while (!pq.isEmpty()) {
maxProfit = Math.max(maxProfit, pq.peek()[1]);
pq.poll();
}
return maxProfit;
}
//Driver Code Starts
public static void main(String[] args) {
int[][] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
System.out.println(maxProfit(jobs));
}
}
//Driver Code Ends
Python
#Driver Code Starts
import heapq
#Driver Code Ends
def maxProfit(jobs):
jobs.sort()
# Min-heap to store {end time, total profit till now}
pq = []
maxProfit = 0
for start, end, profit in jobs:
# Remove jobs that end before current job starts
while pq and pq[0][0] <= start:
maxProfit = max(maxProfit, heapq.heappop(pq)[1])
# Push current job with profit + best profit so far
heapq.heappush(pq, (end, profit + maxProfit))
# Final maximum profit among all chains
while pq:
maxProfit = max(maxProfit, heapq.heappop(pq)[1])
return maxProfit
#Driver Code Starts
if __name__ == "__main__":
jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
]
print(maxProfit(jobs))
#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;
// Min-heap class to store (end time, total profit)
class MinHeap
{
private List<(int, int)> heap = new List<(int, int)>();
private void Swap(int i, int j)
{
var temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
private void HeapifyUp()
{
int i = heap.Count - 1;
while (i > 0)
{
int parent = (i - 1) / 2;
if (heap[parent].Item1 <= heap[i].Item1)
break;
Swap(i, parent);
i = parent;
}
}
private void HeapifyDown()
{
int i = 0;
int n = heap.Count;
while (true)
{
int left = 2 * i + 1;
int right = 2 * i + 2;
int smallest = i;
if (left < n && heap[left].Item1 < heap[smallest].Item1)
smallest = left;
if (right < n && heap[right].Item1 < heap[smallest].Item1)
smallest = right;
if (smallest == i)
break;
Swap(i, smallest);
i = smallest;
}
}
public void Push((int, int) val)
{
heap.Add(val);
HeapifyUp();
}
public (int, int) Pop()
{
var top = heap[0];
var end = heap[heap.Count - 1];
heap.RemoveAt(heap.Count - 1);
if (heap.Count > 0)
{
heap[0] = end;
HeapifyDown();
}
return top;
}
public (int, int) Top()
{
return heap.Count > 0 ? heap[0] : (int.MaxValue, 0);
}
public bool IsEmpty()
{
return heap.Count == 0;
}
}
class GFG {
//Driver Code Ends
// Function to find maximum profit
static int maxProfit(int[,] jobs) {
int n = jobs.GetLength(0);
List<(int, int, int)> list = new List<(int, int, int)>();
for (int i = 0; i < n; i++)
list.Add((jobs[i, 0], jobs[i, 1], jobs[i, 2]));
// Sort jobs by start time
list.Sort((a, b) => a.Item1.CompareTo(b.Item1));
// Min-heap to store {end time, total profit till now}
MinHeap pq = new MinHeap();
int maxProfit = 0;
foreach (var job in list) {
int start = job.Item1, end = job.Item2, profit = job.Item3;
// Remove jobs that end before current job starts
while (!pq.IsEmpty() && pq.Top().Item1 <= start) {
maxProfit = Math.Max(maxProfit, pq.Top().Item2);
pq.Pop();
}
// Push current job with profit + best profit so far
pq.Push((end, profit + maxProfit));
}
// Final maximum profit among all chains
while (!pq.IsEmpty()) {
maxProfit = Math.Max(maxProfit, pq.Top().Item2);
pq.Pop();
}
return maxProfit;
}
//Driver Code Starts
static void Main() {
int[,] jobs = {
{1, 2, 50},
{3, 5, 20},
{6, 19, 100},
{2, 100, 200}
};
Console.WriteLine(maxProfit(jobs));
}
}
//Driver Code Ends
JavaScript
//Driver Code Starts
class MinHeap {
constructor() {
this.heap = [];
}
// Push element and maintain min-heap property
push(val) {
this.heap.push(val);
this._heapifyUp();
}
// Pop smallest element
pop() {
if (this.heap.length === 0) return null;
const top = this.heap[0];
const end = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = end;
this._heapifyDown();
}
return top;
}
// Peek smallest element
top() {
return this.heap.length > 0 ? this.heap[0] : null;
}
// Heapify up
_heapifyUp() {
let i = this.heap.length - 1;
while (i > 0) {
let parent = Math.floor((i - 1) / 2);
if (this.heap[parent][0] <= this.heap[i][0]) break;
[this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];
i = parent;
}
}
// Heapify down
_heapifyDown() {
let i = 0;
const n = this.heap.length;
while (true) {
let left = 2 * i + 1;
let right = 2 * i + 2;
let smallest = i;
if (left < n && this.heap[left][0] < this.heap[smallest][0]) smallest = left;
if (right < n && this.heap[right][0] < this.heap[smallest][0]) smallest = right;
if (smallest === i) break;
[this.heap[i], this.heap[smallest]] = [this.heap[smallest], this.heap[i]];
i = smallest;
}
}
isEmpty() {
return this.heap.length === 0;
}
}
//Driver Code Ends
function maxProfit(jobs) {
jobs.sort((a, b) => a[0] - b[0]);
// Min-heap to store {end time, total profit till now}
const pq = new MinHeap();
let maxProfit = 0;
for (let job of jobs) {
let [start, end, profit] = job;
// Remove jobs that end before current job starts
while (!pq.isEmpty() && pq.top()[0] <= start) {
maxProfit = Math.max(maxProfit, pq.top()[1]);
pq.pop();
}
// Push current job with profit + best profit so far
pq.push([end, profit + maxProfit]);
}
// Final maximum profit among all chains
while (!pq.isEmpty()) {
maxProfit = Math.max(maxProfit, pq.top()[1]);
pq.pop();
}
return maxProfit;
}
//Driver Code Starts
// Driver Code
const jobs = [
[1, 2, 50],
[3, 5, 20],
[6, 19, 100],
[2, 100, 200]
];
console.log(maxProfit(jobs));
//Driver Code Ends
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem