原文链接: https://leetcode-cn.com/problems/minimum-cost-to-hire-k-workers
英文原文
There are n
workers. You are given two integer arrays quality
and wage
where quality[i]
is the quality of the ith
worker and wage[i]
is the minimum wage expectation for the ith
worker.
We want to hire exactly k
workers to form a paid group. To hire a group of k
workers, we must pay them according to the following rules:
- Every worker in the paid group should be paid in the ratio of their quality compared to other workers in the paid group.
- Every worker in the paid group must be paid at least their minimum wage expectation.
Given the integer k
, return the least amount of money needed to form a paid group satisfying the above conditions. Answers within 10-5
of the actual answer will be accepted.
Example 1:
Input: quality = [10,20,5], wage = [70,50,30], k = 2 Output: 105.00000 Explanation: We pay 70 to 0th worker and 35 to 2nd worker.
Example 2:
Input: quality = [3,1,10,10,1], wage = [4,8,2,2,7], k = 3 Output: 30.66667 Explanation: We pay 4 to 0th worker, 13.33333 to 2nd and 3rd workers separately.
Constraints:
n == quality.length == wage.length
1 <= k <= n <= 104
1 <= quality[i], wage[i] <= 104
中文题目
有 N
名工人。 第 i
名工人的工作质量为 quality[i]
,其最低期望工资为 wage[i]
。
现在我们想雇佣 K
名工人组成一个工资组。在雇佣 一组 K 名工人时,我们必须按照下述规则向他们支付工资:
- 对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
- 工资组中的每名工人至少应当得到他们的最低期望工资。
返回组成一个满足上述条件的工资组至少需要多少钱。
示例 1:
输入: quality = [10,20,5], wage = [70,50,30], K = 2 输出: 105.00000 解释: 我们向 0 号工人支付 70,向 2 号工人支付 35。
示例 2:
输入: quality = [3,1,10,10,1], wage = [4,8,2,2,7], K = 3 输出: 30.66667 解释: 我们向 0 号工人支付 4,向 2 号和 3 号分别支付 13.33333。
提示:
1 <= K <= N <= 10000
,其中N = quality.length = wage.length
1 <= quality[i] <= 10000
1 <= wage[i] <= 10000
- 与正确答案误差在
10^-5
之内的答案将被视为正确的。
通过代码
官方题解
方法一:贪心
显然,当我们选择 K
名工人时,会只要有一名工人恰好拿到了他的最低期望工资。因此,我们可以枚举是哪一名工人恰好拿到了他的最低期望工资,并检查在当前的“工资/质量”比值下,其他工人拿到的工资是否不少于他们的最低期望工资。如果有至少 K - 1
名工人满足条件,那么我们就在这些工人中选出 K - 1
名拿到工资最低的,加上枚举的那一名工人的最低期望工资,就得到了一个答案。在所有的答案中,返回最小值。
注意这种方法可能会超出时间限制。
class Solution {
public double mincostToHireWorkers(int[] quality, int[] wage, int K) {
int N = quality.length;
double ans = 1e9;
for (int captain = 0; captain < N; ++captain) {
// Must pay at least wage[captain] / quality[captain] per qual
double factor = (double) wage[captain] / quality[captain];
double prices[] = new double[N];
int t = 0;
for (int worker = 0; worker < N; ++worker) {
double price = factor * quality[worker];
if (price < wage[worker]) continue;
prices[t++] = price;
}
if (t < K) continue;
Arrays.sort(prices, 0, t);
double cand = 0;
for (int i = 0; i < K; ++i)
cand += prices[i];
ans = Math.min(ans, cand);
}
return ans;
}
}
class Solution(object):
def mincostToHireWorkers(self, quality, wage, K):
from fractions import Fraction
ans = float('inf')
N = len(quality)
for captain in xrange(N):
# Must pay at least wage[captain] / quality[captain] per qual
factor = Fraction(wage[captain], quality[captain])
prices = []
for worker in xrange(N):
price = factor * quality[worker]
if price < wage[worker]: continue
prices.append(price)
if len(prices) < K: continue
prices.sort()
ans = min(ans, sum(prices[:K]))
return float(ans)
复杂度分析
时间复杂度:$O(N^2 \log N)$。
空间复杂度:$O(N)$。
方法二:堆(优先队列)
在方法一中,我们枚举了一名工人,并对剩下的工人计算对应的工资,并选出 K - 1
个工资最低的进行累加。事实上,我们可以定义一个“价值”,表示工人最低期望工资与工作质量之比。例如某位工人的最低期望工资为 100
,工作质量为 20
,那么他的价值为 100 / 20 = 5.0
。
可以发现,如果一名工人的价值为 R
,当他恰好拿到最低期望工资时,如果所有价值高于 R
的工人都无法拿到最低期望工资,而所有价值低于 R
的工人都拿得比最低期望工资多。因此,我们可以按照这些工人的价值,对他们进行升序排序。排序后的第 i
名工人可以在它之前任选 K - 1
名工人,并计算对应的工资总和,为 R * sum(quality[c1] + quality[c2] + ... + quality[c{k-1}] + quality[i])
,也就是说,我们需要在前 i
名工人中选择 K
个工作质量最低的。我们可以使用一个大根堆来实时维护 K
个最小值。
class Solution {
public double mincostToHireWorkers(int[] quality, int[] wage, int K) {
int N = quality.length;
Worker[] workers = new Worker[N];
for (int i = 0; i < N; ++i)
workers[i] = new Worker(quality[i], wage[i]);
Arrays.sort(workers);
double ans = 1e9;
int sumq = 0;
PriorityQueue<Integer> pool = new PriorityQueue();
for (Worker worker: workers) {
pool.offer(-worker.quality);
sumq += worker.quality;
if (pool.size() > K)
sumq += pool.poll();
if (pool.size() == K)
ans = Math.min(ans, sumq * worker.ratio());
}
return ans;
}
}
class Worker implements Comparable<Worker> {
public int quality, wage;
public Worker(int q, int w) {
quality = q;
wage = w;
}
public double ratio() {
return (double) wage / quality;
}
public int compareTo(Worker other) {
return Double.compare(ratio(), other.ratio());
}
}
class Solution(object):
def mincostToHireWorkers(self, quality, wage, K):
from fractions import Fraction
workers = sorted((Fraction(w, q), q, w)
for q, w in zip(quality, wage))
ans = float('inf')
pool = []
sumq = 0
for ratio, q, w in workers:
heapq.heappush(pool, -q)
sumq += q
if len(pool) > K:
sumq += heapq.heappop(pool)
if len(pool) == K:
ans = min(ans, ratio * sumq)
return float(ans)
复杂度分析
时间复杂度:$O(N \log N)$。
空间复杂度:$O(N)$。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
3198 | 6821 | 46.9% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|