题目链接
Leetcode.826 安排工作以达到最大收益
rating : 1709
题目描述
你有 n n n 个工作和 m m m 个工人。给定三个数组: d i f f i c u l t y difficulty difficulty, p r o f i t profit profit 和 w o r k e r worker worker ,其中:
- d i f f i c u l t y [ i ] difficulty[i] difficulty[i] 表示第 i i i 个工作的难度, p r o f i t [ i ] profit[i] profit[i] 表示第 i i i 个工作的收益。
- w o r k e r [ i ] worker[i] worker[i] 是第 i 个工人的能力,即该工人只能完成难度小于等于 w o r k e r [ i ] worker[i] worker[i] 的工作。
每个工人 最多 只能安排 一个 工作,但是一个工作可以 完成多次 。
- 举个例子,如果 3 3 3 个工人都尝试完成一份报酬为 $1 的同样工作,那么总收益为 $3 。如果一个工人不能完成任何工作,他的收益为 $0 。
返回 在把工人分配到工作岗位后,我们所能获得的最大利润 。
示例 1:
输入: 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] 的收益。
示例 2:
输入: difficulty = [85,47,57], profit = [24,66,99], worker = [40,25,25]
输出: 0
提示:
- n = d i f f i c u l t y . l e n g t h n = difficulty.length n=difficulty.length
- n = p r o f i t . l e n g t h n = profit.length n=profit.length
- m = w o r k e r . l e n g t h m = worker.length m=worker.length
- 1 ≤ n , m ≤ 1 0 4 1 \leq n, m \leq 10^4 1≤n,m≤104
- 1 ≤ d i f f i c u l t y [ i ] , p r o f i t [ i ] , w o r k e r [ i ] ≤ 1 0 5 1 \leq difficulty[i], profit[i], worker[i] \leq 10^5 1≤difficulty[i],profit[i],worker[i]≤105
解法:排序 + 二分 + 贪心
我们先对 a ( d i f f i c u l t y , p r o f i t ) a(difficulty,profit) a(difficulty,profit) 这样的二元组按照 d i f f i c u l t y difficulty difficulty 从小到大的顺序排序。
假设数据是这样的形式 : ( 68 , 67 ) , ( 35 , 17 ) , ( 52 , 1 ) , ( 47 , 81 ) , ( 86 , 3 ) (68,67),(35,17),(52,1),(47,81),(86,3) (68,67),(35,17),(52,1),(47,81),(86,3)。
那么按照 d i f f i c u l t y difficulty difficulty 从小到大排序之后的数据为: ( 35 , 17 ) , ( 47 , 81 ) , ( 52 , 1 ) , ( 68 , 67 ) , ( 86 , 3 ) (35,17),(47,81),(52,1),(68,67),(86,3) (35,17),(47,81),(52,1),(68,67),(86,3),如下图所示:
目前有一名工人的能力值为
x
x
x,假设当前
x
=
60
x = 60
x=60,那么这名工人可以选择的工作就是前三项。因为已经按照
d
i
f
f
i
c
u
l
t
y
difficulty
difficulty 排序了,所以可以通过二分的方式快速得到某一个工人可以选择工作的范围。
通过观察,可以发现选取第二份工作获得的收益最大,收益为
81
81
81。
那么怎么样才能快速的获取最大收益呢?因为直接排序的时间复杂度是 O ( n 2 × l o g n ) O(n^2 \times logn) O(n2×logn),在 n = 1 0 4 n = 10^4 n=104的数量级下肯定会超时。
因为我们只关注最大收益,其他的收益都可以忽略掉。所以在排序之后,我们可以对 p r o f i t profit profit 做如下的预处理:
在做了以上的预处理操作之后,我们在确定了工人可以选择的工作范围之后,同时也能得到最大收益。此时的最大收益就是工人能够选择最大难度工作对应的收益。
按照上述的例子,工人的能力值 x = 60 x = 60 x=60,他能够选择的工作是前三项,也就是 [ 0 , 2 ] [0,2] [0,2],那么最大收益就是 a [ 2 ] . p r o f i t a[2].profit a[2].profit。
时间复杂度: O ( n × l o g n + n ) O(n \times logn + n) O(n×logn+n)
C++代码:
using PII = pair<int,int>;
class Solution {
public:
int maxProfitAssignment(vector<int>& difficulty, vector<int>& profit, vector<int>& worker) {
int n = difficulty.size();
vector<PII> a;
for(int i = 0;i < n;i++)
{
a.push_back({difficulty[i], profit[i]});
}
ranges::sort(a);
//预处理 只关注最大值
for(int i = 1;i < n;i++)
{
a[i].second = max(a[i].second, a[i - 1].second);
}
auto check = [&](int x){
int l = 0, r = n - 1;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(a[mid].first <= x) l = mid;
else r = mid - 1;
}
return l;
};
int ans = 0;
for(auto x:worker)
{
int i = check(x);
//当前工人困难度最低的工作都没资格选 直接跳过
if(a[i].first > x) continue;
ans += a[i].second;
}
return ans;
}
};