Leetcode 1383 最大的团队表现值(贪心)

Leetcode 1383 最大的团队表现值(贪心)

题目

公司有编号为 1 到 n 的 n 个工程师,给你两个数组 speed 和 efficiency ,其中 speed[i] 和 efficiency[i] 分别代表第 i 位工程师的速度和效率。请你返回由最多 k 个工程师组成的 ​​​​​​最大团队表现值 ,由于答案可能很大,请你返回结果对 10^9 + 7 取余后的结果。

团队表现值 的定义为:一个团队中「所有工程师速度的和」乘以他们「效率值中的最小值」。

示例 1:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 2
输出:60
解释:
我们选择工程师 2(speed=10 且 efficiency=4)和工程师 5(speed=5 且 efficiency=7)。他们的团队表现值为 performance = (10 + 5) * min(4, 7) = 60 。

示例 2:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 3
输出:68
解释:
此示例与第一个示例相同,除了 k = 3 。我们可以选择工程师 1 ,工程师 2 和工程师 5 得到最大的团队表现值。表现值为 performance = (2 + 10 + 5) * min(5, 4, 7) = 68 。

示例 3:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 4
输出:72

提示:

1 <= n <= 10^5
speed.length == n
efficiency.length == n
1 <= speed[i] <= 10^5
1 <= efficiency[i] <= 10^8
1 <= k <= n
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-performance-of-a-team
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题

首先,对题目进行抽象,即求解这么一个问题:
n n n个元素,每个元素都有两个属性,属性A、属性B
现在要从中取 n n n个元素,求一个策略,要求该策略满足:
k k k个元素属性 A A A之和乘以属性 B B B的最小值得到的值最大

进行梳理,发现即满足以下关系式:
F = ∑ ( E i . A ) ∗ m i n { E i . B } , E i ∈ K , K ∈ N F = \sum(E_i.A) * min\{E_i.B\},E_i\in K, K\in N F=(Ei.A)min{ Ei.B}EiKKN
其中 N N N是全集, K K K是元素选取集合, E i E_i Ei K K K中的元素,求 F F F的最大值

这是一个双变量求最值问题, X 1 = ∑ ( E i . A ) X_1=\sum(E_i.A) X1=(Ei.A) X 2 = m i n { E i . B } X_2=min\{E_i.B\} X2=min{ Ei.B} F = X 1 ∗ X 2 F=X_1*X_2 F=X1X2 X 1 X_1 X1 X 2 X_2 X2是非线性相关的

求解此类问题,可以采取先固定一个变量、再选取另一个变量的方法

例如,固定住 X 2 X_2 X2,则 K K K就受到了限制,此时就可以轻易找到对应的 X 1 X_1 X1,从而确定最优的 K K K

现在的问题是,有两个变量,为什么要选择固定 X 2 X_2 X2而非 X 1 X_1 X1

原因之一是,给出 K K K能很容易找到对应的 X 1 X_1 X1 X 2 X_2 X2,而给出 X 1 X_1 X1后却很难枚举所有符合条件的 K K K
另一个原因是,对于容量为 n n n的集合,任意取 k k k个数,最小值的可能取值最多就 k k k个,而和的取值就不一定了。因此,枚举所有的最小值是很容易的,而枚举所有的和就不容易了

因此,题目的大体思路是:
枚举所有的最小效率,并求出对应的最小效率下的最优解,然后取全局最优解

伪代码:

sort(N)  // 对集合N进行排序,使其有序,按效率从大到小排序
Val = 0
totalA = 0
K = NULL
for E in N:  // 由于效率从大到小排序,可以保证最小效率就是当前的元素的效率
	totalA -= min(K.A)
	totalA += E  // 求当前效率下的最大速度
	K.push(E)  // 将E加入集合K
	newVal = totalA * E.B
	Val = max{newVal, Val}  // 更新全局最优解
return Val

第二个问题是,用什么样的数据结构进行解题

由于每次都需要做:取出最小值,放入新值的操作,如果是用数组进行存储,势必要进行大量的移位操作。注意到只需要取出最小值,因此可以维护一个小根堆——小根堆的堆顶必定为最小值,每次都从小根堆的堆顶取出旧值,再push新值

实现代码:

int minHeapPush(int *arr, int size, int e)  // 向小根堆中插入元素,同时删除堆顶
{
    
    
    int res = arr[0];  // 根节点是最小的,因为是小根堆
    arr[0] = e;
    int root = 0, child = 1;
    while (child < size)
    {
    
    
        if (child + 1 < size && arr[child] > arr[child + 1])
            child += 1;
        if (arr[root] < arr[child])
            return res;
        int tmp = arr[root];
        arr[root] = arr[child];
        arr[child] = tmp;
        root = child;
        child = root * 2 + 1;
    }
    return res;
}

int maxPerformance(int n, int* speed, int speedSize, int* efficiency, int efficiencySize, int k)
{
    
    
    // 贪心加堆
    // 第一步,对效率进行排序,使用希尔排序
    for (int gap = n / 2; gap > 0; gap /= 2)
    for (int i = gap; i < n; i ++)
        for (int j = i - gap; j >= 0 && efficiency[j] < efficiency[j + gap]; j -= gap)
        {
    
    
            int tmp_1 = efficiency[j], tmp_2 = speed[j];
            efficiency[j] = efficiency[j + gap]; speed[j] = speed[j + gap];
            efficiency[j + gap] = tmp_1;  speed[j + gap] = tmp_2;
        }
    // 第二步,遍历效率数组,同时维护一个小根堆
    int heap[k];
    memset(heap, 0, sizeof(int) * k);
    long long performance = 0;
    long long totalSpeed = 0;
    for (int i = 0; i < n; i ++)
    {
    
    
        // 效率递减
        totalSpeed += -minHeapPush(heap, k, speed[i]) + speed[i]; // 减掉一个最小的,添加一个新的
        long long tmpPerformance = (long long)totalSpeed * efficiency[i];
        performance = (tmpPerformance > performance) ? tmpPerformance : performance;
    }
    return (int)(performance % (1000000007L));
}

运行结果:

**53 / 53** 个通过测试用例

状态:_通过_

执行用时: **124 ms**

内存消耗: **11.2 MB**

运行速度瓶颈分析:

  1. 使用了希尔排序,且同时对两个数组进行交换,耗费的时间大概是别人的两倍多一点
  2. minHeapPush是一个使用很频繁的函数,如果使用inline关键字或者将其并入主函数,运行速度能得到很大的提升

碰到的问题:

  1. 完全没有思路,看了官方题解才知道怎么写
  2. 忘记了堆排序和shell排序怎么写了
  3. 对于可能溢出int范围的数字处理缺少经验

猜你喜欢

转载自blog.csdn.net/weixin_45206746/article/details/112174682
今日推荐