2071: Maximum-Number-of-Tasks-You-Can-Assign
Hard
This question is quite hard to logically understand…
First, when choosing which tasks to complete, we can either:
Case #1 (from lowest to highest):
There are 2 types of workers you need to account for:
Intuitively speaking, if we are trying to do the lowest strength requirement, for both cases we want to use the lowest strength worker as well.
Unfortunately, the difficulty comes when we are deciding whether to use a normal worker or a pilled worker.
For example, with 1
pill giving strength
3
, in test case #1 below:
workers
=>1, 2, 8
tasks
=>0, 5, 8
or an example with 1
pill giving strength
3
, in test case #2 below:
workers
=>1, 2, 6, 8
tasks
=>0, 5, 11
You can see that the optimal delegation decision changes drastically between #1 and #2:
With test case #2, there is not an easy way to justify
skipping 2, without realising that further down
both the workers
and tasks
array, there
exists a task that can be completed only with a
pill (no easy greedy solution).
Therefore, it might be more feasible to consider the other
case…
Case #2 (from highest to lowest):
Similarly, there are 2 types of workers you need to account for:
Note, there are 2 cases:
Now, while this does work, our strategy changes depending on how many tasks we are trying to achieve:
- e.g. the strongest people will not be
attempting the same tasks in
[0, 1, 3]
when compared to[0, 1, 3, 5, 8]
since this strategy involves attempting the strongest tasks first
Therefore, we can use binary search to check
whether completing the k
easiest tasks is
possible.
Code #1:
class Solution {
public:
bool completeAssignedTasks (const vector<int>& tasks, vector<int> workers, int pills, int strength, int goal) {
for (int i = (int)goal-1; i >= 0; --i) {
if (tasks[i] <= workers.back()) {
.pop_back();
workers} else if (pills > 0) {
bool found = false;
for (int j = 0; j < (int)workers.size(); ++j) {
if (tasks[i] <= workers[j] + strength) {
--pills;
.erase(workers.begin() + j);
workers= true;
found break;
}
}
if (!found) return false;
} else {
return false;
}
}
return true;
}
int maxTaskAssign(vector<int>& tasks, vector<int>& workers, int pills, int strength) {
(tasks.begin(), tasks.end());
sort(workers.begin(), workers.end());
sort
int left = 0, right = min((int) tasks.size(), (int) workers.size());
while (left <= right) {
int middle = left + (right - left) / 2;
if (completeAssignedTasks(tasks, workers, pills, strength, middle)) {
= middle+1;
left } else {
= middle-1;
right }
}
return left-1;
}
};
Originally, I would always iterate from the start of workers to find the worker with the lowest strength to pill.
- it also requires me to usually
pop
elements from the middle of an array which is quite inefficient
However, that is quite inefficient, and we can replace this
O(n^2)
algorithm with an O(n)
by using a
deque
- (I will explain it inside the comments of the code)
Code #2:
class Solution {
public:
bool completeAssignedTasks (const vector<int>& tasks, const vector<int>& workers, int pills, int strength, int goal) {
int w = workers.size()-1;
// NOTE: the FRONT will be STRONGEST workers
// NOTE: the BACK will be WEAKEST workers that can complete tasks with the PILL
<int> dq;
dequefor (int i = (int)goal-1; i >= 0; --i) {
if (!dq.empty() && tasks[i] <= dq.front()) {
// NOTE: contains the STRONGEST workers
.pop_front();
dq} else if (w >= 0 && tasks[i] <= workers[w]) {
// if `dq` is empty, the STRONGEST workers are still in the `workers` array
--w;
} else {
// while there are workers that still exist,
// add all the workers that can complete the task with the pill
while (w >= 0 && tasks[i] <= workers[w] + strength) {
.push_back(workers[w]);
dq--w;
}
// if `dq` is empty, no workers with pill capable of completing the task
// if `pills == 0`, no pills left for workers
if (dq.empty() || pills == 0) {
return false;
}
// the `back` of the `dp` is the most recently added (smallest)
// will be the worker that consumes the pill to complete the task
.pop_back();
dq--pills;
}
}
return true;
}
int maxTaskAssign(vector<int>& tasks, vector<int>& workers, int pills, int strength) {
(tasks.begin(), tasks.end());
sort(workers.begin(), workers.end());
sort
int left = 0, right = min((int) tasks.size(), (int) workers.size());
while (left <= right) {
int middle = left + (right - left) / 2;
if (completeAssignedTasks(tasks, workers, pills, strength, middle)) {
= middle+1;
left } else {
= middle-1;
right }
}
return left-1;
}
};