每日一题 -- LeetCode826

92 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情image.png

前言

每日一题,轻松解题

每日一题为刷题系列 每日刷一题LeetCode题,并且对题目进行分析,分享思路。

正文

:安排工作以达到最大收益

难度:中等

题目要求:

你有 n 个工作和 m 个工人。给定三个数组: difficulty, profit 和 worker ,其中:

  • difficulty[i] 表示第 i 个工作的难度,profit[i] 表示第 i 个工作的收益。

  • worker[i] 是第 i 个工人的能力,即该工人只能完成难度小于等于 worker[i] 的工作。
    每个工人 最多 只能安排 一个 工作,但是一个工作可以 完成多次 。

  • 举个例子,如果 3 个工人都尝试完成一份报酬为 1的同样工作,那么总收益为1 的同样工作,那么总收益为 3 。如果一个工人不能完成任何工作,他的收益为 $0 。 返回 在把工人分配到工作岗位后,我们所能获得的最大利润 。

举个例子

输入: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker = [4,5,6,7]
输出: 100 
解释: 工人被分配的工作难度是 [4,4,6,6] ,分别获得 [20,20,30,30] 的收益。

:解题

方法一 :排序 + 二分查找

二分查找

二分查找算法原理如下:
(1)若待查序列为空,则返回-1,并退出算法;
(2)若待查序列不为空,则将它的中间元素与目标数值进行比较,判断是否相等;
(3)若相等,则返回中间元素索引,并退出算法;此时已查找成功。
(4)若不相等,则比较中间元素与目标数值的大小;
(5)若中间元素 > 目标数值,则将当前序列的前半部分作为新的待查序列;
(6)若中间元素 < 目标数值,则将当前序列的后半部分作为新的待查序列;
(7)在新的序列上重新从第(1)步开始查找。

解题思路:

贪心思想,每个工人完成他能力范围内,收益最大的工作,总收益最大。

将所有工作与收益绑定成一个数组,按难度从小到大排序;
对于每一个工人的能力w,我们可以二分查找到<= w的最后一个工作work[i];
我们要找的是[0, i]范围内工作收益的最大值,因此需要对工作数组预先计算前缀树组,保存每个位置对应的收益最大值;

编辑代码:

var maxProfitAssignment = function(difficulty, profit, worker) {
 
    let works = difficulty.map((i, idx) => [i, profit[idx]]); 
    works.sort((a,b) => a[0] - b[0]);
    let pre = [];
    let max = 0;
    for (let [d,p] of works) {
        max = Math.max(max, p);
        pre.push(max);
    }
    let res = 0;
    for (let w of worker) {
        let l = 0, r = works.length - 1;
        // find last diff <= w.
        while (l < r) {
            let m = Math.ceil((l+r)/2);
            if (works[m][0] <= w) l = m;
            else r = m - 1;
        }
        if (works[l][0] <= w) res += pre[l]; 
    }
    return res;
}; 

总结

无论做什么分析最重要,其中我们分析了题目,分析了解题思路,其实在分析完解题思路后,代码其实就是很简单的事情了,养成习惯,无论做什么之前,都要进行分析,这样有助于你更快更好的完成这件事。