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];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章