当前位置: 首页 > news >正文

LeetCode 2071 你可以安排的最多任务数目 题解(附带自己的错误做题思路 过了25/49)

示例

输入:tasks = [3,2,1], workers = [0,3,3], pills = 1, strength = 1
输出:3
解释:
我们可以按照如下方案安排药丸:
- 给 0 号工人药丸。
- 0 号工人完成任务 2(0 + 1 >= 1)
- 1 号工人完成任务 1(3 >= 2)
- 2 号工人完成任务 0(3 >= 3)

对于本题我的大致思想是通过先对两个数组进行排序,然后对原本数组的tasks和workers进行遍历,让workers中能完成相对于tasks数组中的他能完成的最大值任务数,并将其下标记录入队列,在下次当工人们嗑药后跳过这几个已经由最大值工人们完成的tasks。
光说还是太抽象了,我会大致画一个图来完成我的思想展现。

先通过sort排序两个数组,根据原始数组,从后往前遍历数组tasks,匹配workers数组,用workers数组中的最大值去干掉tasks中能干掉的最大值并将下标记录下来保存至队列queue,在此过程中我们也要记录下使用了workers多少数量的元素,即以下部分代码
sum用来统计原数组可以最大程度解决几个任务

        Arrays.sort(tasks);Arrays.sort(workers);int n = tasks.length;int m = workers.length;int l=0;int sum = 0;int size = 0;Queue<Integer> queue = new LinkedList<>();for(int i=n-1;i>=0&&m-l-1>=0;i--){if(workers[m-l-1]>=tasks[i]){queue.offer(i);System.out.println("i:"+i);System.out.println("Queue elements: " + queue);sum++;l++;size++;}}

之后将栈反转,即使得[1,0]变为[0,1]

        // 使用栈反转队列顺序Stack<Integer> stack = new Stack<>();while (!queue.isEmpty()) {stack.push(queue.poll());}// 将栈中的元素重新放回队列while (!stack.isEmpty()) {queue.offer(stack.pop());}

此时我们给所有工人喂药去匹配任务(你不做,有的是人做😭)

        for(int i=0;i<m;i++){workers[i] = workers[i]+strength;}

然后重新给l和r赋值为0,l决定tasks的遍历程度,r决定workers的遍历程度,如果队列存在,将队列先抛出一个值初始化,进入循环r<m-size&&l<n,是由于workers中已经用掉的不能在使用了,所以减去后面匹配过的长度的子数组,l的话我们就可以通过队列进行筛选,筛选直至队列为空,然后通过判断workers[r]>=tasks[l]&&num>0对药的数量减减,对总的匹配任务加加,并将tasks的边界l右移,且只要是未标记在队列中的值经历过一次这种判断都得使得workers的边界r右移,为了寻找能判断成功的数

        int num = pills;l=0;int r=0;//相等poll,不相等不poll//初始化pollint que = -1;if(!queue.isEmpty()){que = queue.poll();}while(r<m-size&&l<n){if(que==l){if(!queue.isEmpty()){que = queue.poll();}l++;continue;}if(workers[r]>=tasks[l]&&num>0){num--;System.out.println("num:"+num);sum++;System.out.println("sum:"+sum);l++;System.out.println("l:"+l);}r++;}

总的代码如下

class Solution {public int maxTaskAssign(int[] tasks, int[] workers, int pills, int strength) {Arrays.sort(tasks);Arrays.sort(workers);int n = tasks.length;int m = workers.length;int l=0;int sum = 0;int size = 0;Queue<Integer> queue = new LinkedList<>();for(int i=n-1;i>=0&&m-l-1>=0;i--){if(workers[m-l-1]>=tasks[i]){queue.offer(i);sum++;l++;size++;}}// 使用栈反转队列顺序Stack<Integer> stack = new Stack<>();while (!queue.isEmpty()) {stack.push(queue.poll());}// 将栈中的元素重新放回队列while (!stack.isEmpty()) {queue.offer(stack.pop());}for(int i=0;i<m;i++){workers[i] = workers[i]+strength;}int num = pills;l=0;int r=0;//相等poll,不相等不poll//初始化pollint que = -1;if(!queue.isEmpty()){que = queue.poll();}while(r<m-size&&l<n){if(que==l){if(!queue.isEmpty()){que = queue.poll();}l++;continue;}if(workers[r]>=tasks[l]&&num>0){num--;sum++;l++;}r++;}return sum;}
}

以上是我的错误思路,错误的原因在于我是用最大的工人去将能完成的最大任务给完成了,却忽略了当药足够多的时候,最大的工人还能完成更大强度的工作,是结果达到最优,所以我们不能将本题分支开来写,必须一一统计其能完成的最大值。

class Solution {public int maxTaskAssign(int[] tasks, int[] workers, int pills, int strength) {Arrays.sort(tasks);Arrays.sort(workers);int left = 0;int right = Math.min(tasks.length, workers.length) + 1;while (left + 1 < right) {int mid = (left + right) >>> 1;if (check(tasks, workers, pills, strength, mid)) {left = mid;} else {right = mid;}}return left;}private boolean check(int[] tasks, int[] workers, int pills, int strength, int k) {// 贪心:用最强的 k 名工人,完成最简单的 k 个任务Deque<Integer> validTasks = new ArrayDeque<>();int i = 0;for (int j = workers.length - k; j < workers.length; j++) { // 枚举工人int w = workers[j];// 在吃药的情况下,把能完成的任务记录到 validTasks 中while (i < k && tasks[i] <= w + strength) {validTasks.addLast(tasks[i]);i++;}// 即使吃药也无法完成任务if (validTasks.isEmpty()) {return false;}// 无需吃药就能完成(最简单的)任务if (w >= validTasks.peekFirst()) {validTasks.pollFirst();continue;}// 必须吃药if (pills == 0) { // 没药了return false;}pills--;// 完成(能完成的)最难的任务validTasks.pollLast();}return true;}
}

该题解的思路是通过二分,然后一一枚举,找出最大的几个工人和最小的几个任务,然后从最小的工人开始嗑药去匹配,看能匹配到最大的任务是哪个,如果存在不需要吃药就能匹配到的,就直接将该任务抛出,然后继续下一个,并且没有消耗药,如果不存在就说明必须吃药,那就最大程度的利用吃药的这个工人将最大能抛出的值抛出,全部执行完还没return就说明是可以完成当前数量的任务,再增加任务量进行验证可行性,反之减少再验证。

tasks =    [5, 9, 8, 5, 9]
workers = [1, 6, 4, 2, 6]
pills = 1
strength = 5

排序

tasks    = [5, 5, 8, 9, 9]
workers  = [1, 2, 4, 6, 6]

Step 2: 二分查找答案

我们要找出 最多能完成多少个任务

left = 0right = min(5,5)+1 = 6

我们开始二分:

尝试 mid = 3

check(tasks, workers, pills=1, strength=5, k=3)

  • 取最强的 3 个工人:[4, 6, 6]

  • 取最简单的 3 个任务:[5, 5, 8]

初始化:

  • validTasks = []

  • i = 0

第 1 位工人:w = 4

  • 能力 + 药 = 4 + 5 = 9

  • 所以 tasks[0] = 5, tasks[1] = 5, tasks[2] = 8 都可加入:
    validTasks = [5, 5, 8]

  • w = 4 不能直接完成 5

  • 所以吃药,药数变为 0,完成最难任务 8
    validTasks = [5, 5]

第 2 位工人:w = 6

  • 直接可以完成 5,不用吃药
    validTasks = [5]

第 3 位工人:w = 6

  • 直接完成 5
    validTasks = []

✅ 成功完成 3 个任务 → 返回 trueleft = 3

尝试 mid = 4

check(tasks, workers, pills=1, strength=5, k=4)

  • 工人:[2, 4, 6, 6]

  • 任务:[5, 5, 8, 9]

初始化:

  • validTasks = []

  • i = 0

第 1 位工人:w = 2

  • 能力+药=7,可以加入:5,5(8太大)
    validTasks = [5,5]

  • 不能完成任何任务,只能吃药:完成最难的 5
    pills = 0validTasks = [5]

第 2 位工人:w = 4

  • 能完成任务 5
    validTasks = []

第 3 位工人:w = 6

  • 继续推进 itasks[2] = 8, tasks[3] = 9 都能入队(能力+药=11)
    validTasks = [8,9]

  • 能力不够完成 8,但没药了 ❌ → 失败

返回 false → right = 4

尝试 mid = 3 已知成功

尝试 mid = 4 失败

最终 left = 3,即最多 完成 3 个任务

相关文章:

  • 个人健康中枢的多元化AI网络革新与精准健康路径探析
  • 【数据结构】励志大厂版·初阶(复习+刷题)排序
  • linux 使用nginx部署ssl证书,将http升级为https
  • CF1000E We Need More Bosses
  • 什么是DGI数据治理框架?
  • 【Python】一直没搞懂生成器是什么。。
  • Fine Structure-Aware Sampling(AAAI 2024)论文笔记和启发
  • Milvus(13):自定义分析器、过滤器
  • Prompt compress 技术探究-LLMLingua
  • 【Linux】深入理解程序地址空间
  • WPF中Behaviors
  • [特殊字符] 人工智能大模型之开源大语言模型汇总(国内外开源项目模型汇总) [特殊字符]
  • leetcode 59. 螺旋矩阵 II
  • 大连理工大学选修——图形学:第二章 计算机图形硬件及绘图系统
  • Git推送大文件导致提交回退的完整解决记录
  • 短信侠 - 自建手机短信转发到电脑上并无感识别复制验证码,和找手机输验证码说再见!
  • LLM论文笔记 28: Universal length generalization with Turing Programs
  • 365打卡第R6周: LSTM实现糖尿病探索与预测
  • idea结合CopilotChat进行样式调整实践
  • [pdf,epub]292页《分析模式》漫谈合集01-59提供下载
  • 建邦高科赴港上市,大股东陈子淳系山东建邦集团董事长陈箭之子
  • 全国铁路迎来返程客流高峰,预计今日发送2040万人次
  • 海港通报颜骏凌伤停两至三周,国足面临门将伤病危机
  • 印尼巴厘岛多地停电,疑似海底电缆发生故障
  • 人民日报:上海“模速空间”何以汇聚超百家大模型企业
  • 上海浪琴环球马术冠军赛明日启幕!五一假期在这里感受精彩