You are given an integer array jobs
, where jobs[i]
is the amount of time it takes to complete the i<sup>th</sup>
job.
There are k
workers that you can assign jobs to. Each job should be assigned to exactly one worker. The working time of a worker is the sum of the time it takes to complete all jobs assigned to them. Your goal is to devise an optimal assignment such that the maximum working time of any worker is minimized.
Return the minimum possible maximum working time of any assignment.
Example 1:
Input: jobs = [3,2,3], k = 3
Output: 3
Explanation: By assigning each person one job, the maximum time is 3.
Example 2:
Input: jobs = [1,2,4,7,8], k = 2
Output: 11
Explanation: Assign the jobs the following way:
Worker 1: 1, 2, 8 (working time = 1 + 2 + 8 = 11)
Worker 2: 4, 7 (working time = 4 + 7 = 11)
The maximum working time is 11.
Constraints:
1 <= k <= jobs.length <= 12
1 <= jobs[i] <= 10<sup>7</sup>
This problem is about finding the optimal way to assign jobs to workers such that the maximum working time of any worker is minimized. This can be solved using binary search combined with a backtracking approach to check if a given maximum working time is feasible.
One brute-force approach would be to try all possible assignments of jobs to workers and calculate the maximum working time for each assignment. This can be done by generating all possible partitions of the jobs
array into k
subsets and then calculating the maximum sum of each subset. The minimum of these maximum sums would be the answer.
This approach is highly inefficient due to the exponential number of possible assignments. Generating all possible partitions is computationally expensive, making it impractical for larger input sizes.
The optimal solution involves using binary search to find the minimum possible maximum working time. We can define a search space where the lower bound is the maximum job length, and the upper bound is the sum of all job lengths. For each value (potential maximum working time) in this space, we use a backtracking algorithm to determine if it's possible to assign all jobs to k
workers such that no worker exceeds this maximum working time.
Algorithm:
low
is the maximum value in the jobs
array. The upper bound high
is the sum of all values in the jobs
array.[low, high]
.mid
value during binary search, check if it's possible to assign jobs such that no worker's workload exceeds mid
using a backtracking algorithm.high = mid - 1
.low = mid + 1
.low > high
), low
will be the minimum possible maximum working time.Code Implementation (Java):
public class AssignJobs {
public int minimizeMax(int[] jobs, int k) {
int low = 0, high = 0;
for (int job : jobs) {
low = Math.max(low, job);
high += job;
}
int ans = high;
while (low <= high) {
int mid = (low + high) / 2;
if (isPossible(jobs, k, mid)) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
private boolean isPossible(int[] jobs, int k, int maxLoad) {
int[] workers = new int[k];
return backtrack(jobs, workers, 0, maxLoad);
}
private boolean backtrack(int[] jobs, int[] workers, int jobIndex, int maxLoad) {
if (jobIndex == jobs.length) {
return true;
}
for (int i = 0; i < workers.length; i++) {
if (workers[i] + jobs[jobIndex] <= maxLoad) {
workers[i] += jobs[jobIndex];
if (backtrack(jobs, workers, jobIndex + 1, maxLoad)) {
return true;
}
workers[i] -= jobs[jobIndex]; // Backtrack
}
// Optimization: If the worker is empty, assigning the job to another empty worker
// won't lead to a different result. Prevents duplicate assignments.
if (workers[i] == 0) break;
}
return false;
}
public static void main(String[] args) {
AssignJobs solution = new AssignJobs();
// Example 1
int[] jobs1 = {3, 2, 3};
int k1 = 3;
System.out.println("Example 1: " + solution.minimizeMax(jobs1, k1)); // Output: 3
// Example 2
int[] jobs2 = {1, 2, 4, 7, 8};
int k2 = 2;
System.out.println("Example 2: " + solution.minimizeMax(jobs2, k2)); // Output: 11
// Example 3
int[] jobs3 = {5, 5, 5, 4, 6};
int k3 = 4;
System.out.println("Example 3: " + solution.minimizeMax(jobs3, k3)); // Output: 6
}
}
O(log(Sum - Max))
where Sum
is the sum of all job lengths and Max
is the maximum job length. This provides the logarithmic factor of the overall time complexity.k
, and we have n
jobs. Thus, in the worst case, the backtracking might take O(k^n) time. However, the optimizations and pruning help reduce the average time.O(log(Sum - Max) * k^n)
in the worst case, where n
is the number of jobs and k
is the number of workers. Because n
is small (n <= 12), this is generally acceptable. The efficiency primarily depends on how effectively the backtracking is optimized.workers
array in the isPossible
function requires O(k)
space to track the current load of each worker.n
(number of jobs).O(k + n)
, where k
is the number of workers, and n
is the number of jobs. The space is mainly used for storing the workers
array and the recursion call stack.k = 1
: If there is only one worker, the minimum maximum working time is simply the sum of all job lengths. The binary search will still work correctly, but it is an important edge case to consider.k >= n
: If the number of workers is greater than or equal to the number of jobs, we can assign each job to a separate worker. In this case, the minimum maximum working time is the maximum job length. This is naturally handled by the binary search because the lower bound is set to the maximum job length.jobs
array, but it will behave reasonably as the low
variable will remain 0, and the while loop will not execute, directly returning ans = 0
.long
data type would be better when computing the sum and performing binary search.