GuilinDev

Lc1882

05 August 2008

1882. Process Tasks Using Servers

给你两个 下标从 0 开始 的整数数组 servers 和 tasks ,长度分别为 n​​​​​​ 和 m​​​​​​ 。servers[i] 是第 i​​​​​​​​​​ 台服务器的 权重 ,而 tasks[j] 是处理第 j​​​​​​ 项任务 所需要的时间(单位:秒)。

你正在运行一个仿真系统,在处理完所有任务后,该系统将会关闭。每台服务器只能同时处理一项任务。第 0 项任务在第 0 秒可以开始处理,相应地,第 j 项任务在第 j 秒可以开始处理。处理第 j 项任务时,你需要为它分配一台 权重最小 的空闲服务器。如果存在多台相同权重的空闲服务器,请选择 下标最小 的服务器。如果一台空闲服务器在第 t 秒分配到第 j 项任务,那么在 t + tasks[j] 时它将恢复空闲状态。

如果没有空闲服务器,则必须等待,直到出现一台空闲服务器,并 尽可能早 地处理剩余任务。 如果有多项任务等待分配,则按照 下标递增 的顺序完成分配。

如果同一时刻存在多台空闲服务器,可以同时将多项任务分别分配给它们。

构建长度为 m 的答案数组 ans ,其中 ans[j] 是第 j 项任务分配的服务器的下标。

返回答案数组 ans​​​​ 。

优先队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class Solution {

    public int[] assignTasks(int[] servers, int[] tasks) {
        //定义优先队列sq1(存入long[]{服务器标号 , 权重}),并重写Comparator(权重从小到大,标号从小到大)
        PriorityQueue<long[]> pq1 = new PriorityQueue<long[]>((a, b) -> {
            if (a[1] == b[1]) {
                return (int) (a[0] - b[0]);
            }
            return (int) (a[1] - b[1]);
        });
        //定义优先队列sq2(存入long[]{服务器标号 , 权重 , 服务器完成工作时间}),并重写Comparator(完成工作时间从小到大)
        PriorityQueue<long[]> pq2 = new PriorityQueue<long[]>(new Comparator<long[]>() {
            public int compare(long[] a, long[] b) {
                return (int) (a[2] - b[2]);
            }
        });
        for (int i = 0; i < servers.length; i++) {
            pq1.offer(new long[]{i, servers[i]});
        }
        int len = tasks.length;
        int[] result = new int[len];
        int r = 0;
        Deque<Integer> lst = new LinkedList<>();
        for (int i = 0; i < len; i++) {
            //检测sq2中是否有 完成时间小于<=当前时间的服务器,若有,则从sq2中取出该服务器并加入sq1中
            while (!pq2.isEmpty() && pq2.peek()[2] <= i) {
                long[] tas = pq2.poll();
                pq1.offer(new long[]{tas[0], tas[1]});
            }
            //任务从尾部进入队列
            lst.offerLast(tasks[i]);
            //当sq1和lst都不为空时,说明有任务可以加到服务器中,此时从lst头部取出任务,并从sq1中取出服务器,两者结合后添加到sq2中
            //题目原话:如果同一时刻存在多台空闲服务器,可以同时将多项任务分别分配给它们。(很重要)
            while (!pq1.isEmpty() && !lst.isEmpty()) {
                long[] ser = pq1.poll();
                result[r++] = (int) ser[0];
                pq2.offer(new long[]{ser[0], ser[1], i + lst.pollFirst()});
            }
        }
        long t = len;
        //若是lst依旧不为空,说明服务器资源已满,需要等待
        while (!lst.isEmpty()) {
            //因此我们取出完成时间最小的所有服务器(多个服务器可能会同时解放)
            if (!pq2.isEmpty()) {
                //需把时间t置为服务器解放时间(很多超时是因为t逐一累加)
                t = pq2.peek()[2];
                while (!pq2.isEmpty() && pq2.peek()[2] == t) {
                    long[] tas = pq2.poll();
                    pq1.offer(new long[]{tas[0], tas[1]});
                }
            }
            //仿照上面把任务添加进空闲服务器
            while (!pq1.isEmpty() && !lst.isEmpty()) {
                long[] ser = pq1.poll();
                result[r++] = (int) ser[0];
                pq2.offer(new long[]{ser[0], ser[1], t + lst.pollFirst()});
            }
        }
        return result;
    }
}