LeetCode1383. 最大的团队表现值(PriorityQueue的使用)

公司有编号为 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

解答:
先说一下这道题吧,这道题的思路是:按照效率把所有的工程师从高到低进行排序,这样我们数到哪个工程师,这个工程师的效率就是计算效率。
怎么获得所有的速度和呢?我们把所有的速度放到一个集合里面,但是这个集合得是有序的,因为加入太多的值之后,我们要移除最小的值,这样才能保证留下的速度和最大。
我们依次计算所有可能性的结果,那最大的结果就找到了。
对工程师按照效率排序不难,难的就是保证速度集合有序。
自以为聪明的我,想到了链表,因为如果使用数组,每次插入一个值,整个数组移动太大。
但是链表要找到插在哪,是不是有很困难,我又加入TreeMap,找到我插在谁的后面。
这样,当链表过长时,把首节点移除。保证剩下的速度和最大。
代码1)就是这个思路。
执行完毕,我去,110ms,仅比不到10%的强。看了一下比较快的代码。PriorityQueue,我好像有点印象,真想抽一下自己。
把代码进行优化,看代码2)
不过我还是很好奇,它是怎么做到每次取出来的都是最小呢?我看了源码,说实话,有点云里雾里,但是我被一个方法的单词吸引了,heapify,查了一下,是数组建堆,我立马就醒悟了。对啊,我说这<<<的在干嘛,这不就是和上一个父节点比较嘛
看图就明白了。

      0
     / \
    1   2
   / \ / \
  3  4 5  6
 / \
7   8

我数组建成堆之后,第7位的父节点是3,第2位的子节点是5和6,这样立即明白(k-1)>>>1是在干嘛了,不就是找父节点吗。
我们只要保证,子节点永远不小于父节点,这样根节点就永远最小了
那我自己建堆行不行?我的Queue比较简单,目标对象就是int类型的,数组长度也固定,不需要判断扩容,甚至,我都可以不用new Queue操作。
代码见代码3)
后面我会写一篇PriorityQueue的源码阅读,flag先立在这里。
最近学习斐波那契堆,感觉可以在这用
算法导论第19章-斐波那契堆的学习笔记(以及LeetCode1383的斐波那契堆的做法)

代码1)

long allNum = 0;
Node head;
TreeMap<Integer, Node> nodeTreeMap = new TreeMap<>();
long NN = 1000000;

public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
    if (k == 0) {
        return 0;
    }

    Long[] effSpee = new Long[n];
    for (int i = 0; i < n; i++) {
        effSpee[i] = efficiency[i] * NN + speed[i];
    }
    Arrays.sort(effSpee, new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return o2.compareTo(o1);
        }
    });
    head = new Node((int) (effSpee[0] % NN));
    nodeTreeMap.put(head.value, head);
    allNum += head.value;
    long ans = (effSpee[0] / NN) * allNum;
    int sta = 1;
    while (sta < k) {
        long v = (effSpee[sta] / NN);
        int nspeed = (int) (effSpee[sta] % NN);
        allNum += nspeed;
        Node nNode = new Node(nspeed);
        if (nspeed <= head.value) {
            nNode.next = head;
            head = nNode;
        } else {
            Node lefNode = nodeTreeMap.floorEntry(nspeed).getValue();
            nNode.next = lefNode.next;
            lefNode.next = nNode;
        }
        nodeTreeMap.put(nspeed, nNode);
        sta++;
        ans = Math.max(ans, v * allNum);
    }
    for (int i = k; i < n; i++) {
        long v = (effSpee[i] / NN);
        if (v == 0) {
            return (int) (ans % 1000000007);
        }
        int nspeed = (int) (effSpee[i] % NN);
        if (nspeed <= head.value) {
            continue;
        }
        allNum += nspeed;
        allNum -= head.value;
        Node nNode = new Node(nspeed);
        Node lefNode = nodeTreeMap.floorEntry(nspeed).getValue();
        nNode.next = lefNode.next;
        lefNode.next = nNode;
        nodeTreeMap.put(nspeed, nNode);
        head = head.next;
        ans = Math.max(ans, v * allNum);
    }
    return (int) (ans % 1000000007);
}

class Node {
    int value;
    Node next;

    public Node(int speed) {
        value = speed;
    }
}

代码2)

long NN = 1000000;

public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
    if (k == 0) {
        return 0;
    }
    Long[] effSpee = new Long[n];
    for (int i = 0; i < n; i++) {
        effSpee[i] = efficiency[i] * NN + speed[i];
    }
    Arrays.sort(effSpee, new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return o2.compareTo(o1);
        }
    });

    PriorityQueue<Integer> integerPriorityQueue = new PriorityQueue<>(k);
    long allNum = 0;
    long ans = 0;
    for (int i = 0; i < n; i++) {
        int nspeed = (int) (effSpee[i] % NN);
        long v = (effSpee[i] / NN);
        allNum += nspeed;
        integerPriorityQueue.add(nspeed);
        if (i >= k) {
            allNum -= integerPriorityQueue.poll();
        }
        ans = Math.max(ans, v * allNum);
    }
    return (int) (ans % 1000000007);
}

代码3)

long NN = 1000000;

public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
    if (k == 0) {
        return 0;
    }
    Long[] effSpee = new Long[n];
    for (int i = 0; i < n; i++) {
        effSpee[i] = efficiency[i] * NN + speed[i];
    }
    Arrays.sort(effSpee, new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return o2.compareTo(o1);
        }
    });

    len = k;
    values = new int[len];
    add((int) (effSpee[0] % NN));
    long allNum = (int) (effSpee[0] % NN);
    long ans = (effSpee[0] / NN) * allNum;
    for (int i = 1; i < n; i++) {
        int nspeed = (int) (effSpee[i] % NN);
        long v = (effSpee[i] / NN);
        if (i >= k) {
            if (nspeed <= peek()) {
                continue;
            } else {
                allNum -= poll();
            }
        }
        allNum += nspeed;
        add(nspeed);
        ans = Math.max(ans, v * allNum);
    }
    return (int) (ans % 1000000007);
}

int size = 0;
int len = 16;
int[] values;

public void add(int v) {
    values[size] = v;
    if (size > 0) {
        checkAdd(size, v);
    }
    size++;
}

private void checkAdd(int k, int v) {
    while (k > 0) {
        int p = (k - 1) >>> 1;
        if (values[p] < v) {
            break;
        }
        values[k] = values[p];
        k = p;
    }
    values[k] = v;
}

public int poll() {
    int ans = values[0];
    values[0] = values[--size];
    checkRemove(0, values[0]);
    return ans;
}

private void checkRemove(int k, int v) {
    int half = size >>> 1;
    while (k < half) {
        int left = 2 * k + 1;
        int right = 2 * k + 2;
        if (v <= values[left] && v <= values[right]) {
            break;
        }
        if (values[left] < values[right]) {
            values[k] = values[left];
            k = left;
        } else {
            values[k] = values[right];
            k = right;
        }
    }
    values[k] = v;
}

public int peek() {
    return values[0];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章