一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
前言
每日一题,轻松解题
每日一题为刷题系列 每日刷一题LeetCode题,并且对题目进行分析,分享思路。
正文
:安排工作以达到最大收益
难度:中等
题目要求:
你有 n 个工作和 m 个工人。给定三个数组: difficulty, profit 和 worker ,其中:
-
difficulty[i] 表示第 i 个工作的难度,profit[i] 表示第 i 个工作的收益。
-
worker[i] 是第 i 个工人的能力,即该工人只能完成难度小于等于 worker[i] 的工作。
每个工人 最多 只能安排 一个 工作,但是一个工作可以 完成多次 。 -
举个例子,如果 3 个工人都尝试完成一份报酬为 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;
};
总结
无论做什么分析最重要,其中我们分析了题目,分析了解题思路,其实在分析完解题思路后,代码其实就是很简单的事情了,养成习惯,无论做什么之前,都要进行分析,这样有助于你更快更好的完成这件事。