GeeksforGeeks Hard

Job Sequencing Problem

Each job takes 1 unit of time, has a deadline and a profit. Maximize profit by scheduling at most one job per slot, each before its deadline.

greedysortingscheduling
Open on GeeksforGeeks ↗
02

Intuition

Take jobs in profit order — but place each as late as its deadline allows. Placing late keeps earlier slots free for jobs with tighter deadlines, so a rich job never squeezes out another it didn't have to.

How to spot this pattern

Greedy with a twist: sort by profit descending, then place each job as late as its deadline allows. Scheduling late keeps the early slots free for jobs that might have tighter deadlines. Whenever a greedy choice must also preserve room for future choices, ask which placement is least disruptive — that's usually the latest legal one.

03

Approach

1

Sort by profit, descending

If a job can be scheduled at all, we'd rather it be a high-profit one — commit to the richest first.

2

Place each in its latest free slot

For a job with deadline d, scan slots d, d−1, … 1 for a free one. Late placement preserves options for tighter jobs.

3

Optional speed-up

The slot scan can use a union-find 'next free slot' structure to reach O(n log n); the simple scan is O(n·maxD).

04

Solution & live demo

1def job_sequencing(jobs): # jobs: [(id, deadline, profit)]
2 jobs.sort(key=lambda j: -j[2])
3 max_d = max(j[1] for j in jobs)
4 slot = [None] * (max_d + 1) # slot[t] = job id, 1-indexed
5 count = profit = 0
6 for jid, d, p in jobs:
7 for t in range(min(d, max_d), 0, -1):
8 if slot[t] is None:
9 slot[t] = jid
10 count += 1; profit += p
11 break
12 return count, profit
05

Common pitfalls

Sorting by deadline instead of profit

✗ Wrong
jobs.sort(key=lambda j: j[1])
✓ Right
jobs.sort(key=lambda j: -j[2])

Slots are the scarce resource, so the greedy must spend them on the most valuable jobs first. Ordering by deadline fills early slots with whatever happens to be urgent, and a high-profit job arriving later finds nothing free.

Placing each job at the earliest free slot

✗ Wrong
for t in range(1, min(d, max_d) + 1):
    if slot[t] is None: ...
✓ Right
for t in range(min(d, max_d), 0, -1):
    if slot[t] is None: ...

Taking an early slot for a job with a distant deadline steals the only slot a tight-deadline job could ever use. Scanning backwards from the deadline leaves the maximum room for everything still to come.

Sizing the slot array by job count

✗ Wrong
slot = [None] * (len(jobs) + 1)
✓ Right
max_d = max(j[1] for j in jobs)
slot = [None] * (max_d + 1)

Deadlines are time units, not job indices, and a deadline may exceed the number of jobs. The array must span the largest deadline, and the + 1 keeps it 1-indexed so slot numbers read as times.

06

Edge cases

All deadlines = 1

Only one slot exists — the single most profitable job is chosen.

More jobs than slots

Jobs failing to find a free slot are skipped, by construction the cheapest ones.

07

Complexity

Time
O(n log n + n·D)
Space
O(D)
D = max deadline; union-find drops the second term.