题目:
分析题目:
看题目后,第一反应是贪心,即要求最高力量值的任务,分配给最强大的工人,但由于有药丸存在,需要考虑第二种情况,即有些工人吃下药丸后,能够完成某些任务,那么这时候,如何分配药丸就成了关键(分配给哪个工人,该工人又去做哪个任务)
假设答案是k,那么应该是最小的k个任务和力量值最大的k个工人
出于便利,将数组排序后,对大小关系更容易处理
分配思路(最大的那个任务为t):
- 若当前工人中有能够完成该任务的,选择让值最大的工人去完成
- 若当前工人中没有能够完成该任务的,那就应该让吃药了后,力量值最小的工人去完成(尽量完美利用药片)
那么该如何找到k?
任务已经排好序,那么可以使用二分法来枚举,取到的mid便是可能的最大任务数,校验是否能够被工人做完即可
代码解析:
双端队列的用处是便于找到最大力量的工人和能够吃药的最小力量工人
class Solution {
public int maxTaskAssign(int[] tasks, int[] workers, int pills, int strength) {
Arrays.sort(tasks);
Arrays.sort(workers);
int min = 0;
int max = Math.min(tasks.length, workers.length);
// 二分法
while (min < max) {
int mid = (min + max + 1) >> 1;
if(check(mid,tasks,workers,pills,strength)){
min = mid;
}else {
max = mid-1;
}
}
return min;
}
public boolean check(int mid, int[] tasks,int[] workers,int pills, int strength){
Deque<Integer> queue = new ArrayDeque<>();
int index = workers.length - 1;
//task任务递减,works也递减
for (int i = mid - 1; i >= 0; i--) {
int task = tasks[i];
// 每次循环队列只在队尾增加最小值。
while (index >= 0 && workers[index] + strength >= task) {
queue.addLast(workers[index]);
// index始终在减少,已经入队的不会重新入队,只入队满足条件的
index--;
}
// 吃了药丸都完成不了工作
if (queue.size() == 0) {
return false;
}
// 队伍最大力量不吃药丸的可以完成工作
if(queue.getFirst() >= task){
queue.removeFirst();
}else if(pills -- > 0 ){
// 需要吃药丸时,让队尾最小力量的工人吃药
queue.removeLast();
}else {
return false;
}
}
return true;
}
}