You want to schedule a list of jobs in d
days. Jobs are dependent (i.e To work on the i<sup>th</sup>
job, you have to finish all the jobs j
where 0 <= j < i
).
You have to finish at least one task every day. The difficulty of a job schedule is the sum of difficulties of each day of the d
days. The difficulty of a day is the maximum difficulty of a job done on that day.
You are given an integer array jobDifficulty
and an integer d
. The difficulty of the i<sup>th</sup>
job is jobDifficulty[i]
.
Return the minimum difficulty of a job schedule. If you cannot find a schedule for the jobs return -1
.
For example:
jobDifficulty = [6,5,4,3,2,1], d = 2
Output: 7
Explanation: First day you can finish the first 5 jobs, total difficulty = 6. Second day you can finish the last job, total difficulty = 1. The difficulty of the schedule = 6 + 1 = 7
As another example:
jobDifficulty = [9,9,9], d = 4
Output: -1
Explanation: If you finish a job per day you will still have a free day. you cannot find a schedule for the given jobs.
How would you approach solving this problem efficiently?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for the job scheduling problem involves checking every conceivable way to divide the jobs into days. We will consider every possible combination of assigning jobs to days to find the one with the least difficulty. It's like trying out every possible schedule until we find the best one.
Here's how the algorithm would work step-by-step:
def minimum_difficulty_brute_force(job_difficulty, days):
number_of_jobs = len(job_difficulty)
# If there are fewer jobs than days, it's impossible to schedule.
if number_of_jobs < days:
return -1
minimum_total_difficulty = float('inf')
def calculate_difficulty(schedule):
total_difficulty = 0
for day_schedule in schedule:
total_difficulty += max(job_difficulty[job_index] for job_index in day_schedule)
return total_difficulty
def generate_schedules(current_schedule, job_index_to_assign):
nonlocal minimum_total_difficulty
# Base case: all jobs have been assigned.
if job_index_to_assign == number_of_jobs:
if len(current_schedule) == days:
difficulty = calculate_difficulty(current_schedule)
minimum_total_difficulty = min(minimum_total_difficulty, difficulty)
return
# Try adding the job to the last day.
if current_schedule:
current_schedule[-1].append(job_index_to_assign)
generate_schedules(current_schedule, job_index_to_assign + 1)
current_schedule[-1].pop()
# Try starting a new day with the job.
# This explores splitting the jobs into more days.
if len(current_schedule) < days:
generate_schedules(current_schedule + [[job_index_to_assign]], job_index_to_assign + 1)
# Start the recursion with an empty schedule.
generate_schedules([], 0)
# Return the minimum difficulty found.
return minimum_total_difficulty
The goal is to split a list of jobs into a specific number of days, minimizing the overall difficulty. We use a method that remembers the best solutions to subproblems, avoiding recomputation and leading to an efficient result.
Here's how the algorithm would work step-by-step:
def minimum_difficulty_of_a_job_schedule(job_difficulty, days_to_schedule):
number_of_jobs = len(job_difficulty)
if days_to_schedule > number_of_jobs:
return -1
# difficulty_by_job_and_day[i][d] represents the min difficulty to schedule the first i jobs in d days.
difficulty_by_job_and_day = [[float('inf')] * (days_to_schedule + 1) for _ in range(number_of_jobs + 1)]
difficulty_by_job_and_day[0][0] = 0
for job_index in range(1, number_of_jobs + 1):
for day_index in range(1, days_to_schedule + 1):
maximum_difficulty = 0
# Iterate backwards to find the best split
for k_index in range(job_index, 0, -1):
maximum_difficulty = max(maximum_difficulty, job_difficulty[k_index - 1])
# Accumulate the maximum difficulty for each day
difficulty_by_job_and_day[job_index][day_index] = min(difficulty_by_job_and_day[job_index][day_index], \
difficulty_by_job_and_day[k_index - 1][day_index - 1] + maximum_difficulty)
# Return the minimum difficulty for the entire schedule
return difficulty_by_job_and_day[number_of_jobs][days_to_schedule]
Case | How to Handle |
---|---|
jobs array is null or empty | Return 0 if d is also 0, otherwise return -1 because no schedule is possible. |
d is greater than the number of jobs | Return -1, as it's impossible to have more days than jobs. |
d is 1 (single day) | The minimum difficulty is the maximum difficulty of all jobs. |
jobs array contains all identical values | The algorithm should still correctly partition the jobs and calculate the minimum difficulty, potentially leading to identical difficulties on different days if 'd' allows. |
jobs array contains very large difficulty values | Ensure the data type used for calculations (e.g., int) can handle the magnitude of the difficulty values to prevent integer overflow, possibly using long. |
jobs array contains negative difficulty values (if not explicitly disallowed in the problem statement) | Return -1 if the job difficulties are all negative and d > 0, otherwise throw an exception or handle depending on problem constraints if negative values are disallowed; the DP array should use sufficiently large values, or initialize to infinity, not zero. |
Maximum size of jobs array and/or large value of d (scalability) | Dynamic programming solution has time complexity O(n*n*d), so consider memory usage and runtime limits; potentially reduce space complexity with memoization. |
No valid solution exists because the minimum number of jobs required per day exceeds the total number of jobs. | The algorithm returns -1 when no valid schedule can be constructed because the number of days is greater than the number of jobs. |