题目描述
给你两个 下标从 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:
输入:servers = [3,3,2], tasks = [1,2,3,2,1,2]
输出:[2,2,0,2,1,2]
解释:事件按时间顺序如下:
- 0 秒时,第 0 项任务加入到任务队列,使用第 2 台服务器处理到 1 秒。
- 1 秒时,第 2 台服务器空闲,第 1 项任务加入到任务队列,使用第 2 台服务器处理到 3 秒。
- 2 秒时,第 2 项任务加入到任务队列,使用第 0 台服务器处理到 5 秒。
- 3 秒时,第 2 台服务器空闲,第 3 项任务加入到任务队列,使用第 2 台服务器处理到 5 秒。
- 4 秒时,第 4 项任务加入到任务队列,使用第 1 台服务器处理到 5 秒。
- 5 秒时,所有服务器都空闲,第 5 项任务加入到任务队列,使用第 2 台服务器处理到 7 秒。
示例 2:
输入:servers = [5,1,4,3,2], tasks = [2,1,2,4,5,2,1]
输出:[1,4,1,4,1,3,2]
解释:事件按时间顺序如下:
- 0 秒时,第 0 项任务加入到任务队列,使用第 1 台服务器处理到 2 秒。
- 1 秒时,第 1 项任务加入到任务队列,使用第 4 台服务器处理到 2 秒。
- 2 秒时,第 1 台和第 4 台服务器空闲,第 2 项任务加入到任务队列,使用第 1 台服务器处理到 4 秒。
- 3 秒时,第 3 项任务加入到任务队列,使用第 4 台服务器处理到 7 秒。
- 4 秒时,第 1 台服务器空闲,第 4 项任务加入到任务队列,使用第 1 台服务器处理到 9 秒。
- 5 秒时,第 5 项任务加入到任务队列,使用第 3 台服务器处理到 7 秒。
- 6 秒时,第 6 项任务加入到任务队列,使用第 2 台服务器处理到 7 秒。
提示:
servers.length == n
tasks.length == m
1 <= n, m <= 2 * 105
1 <= servers[i], tasks[j] <= 2 * 105
解题思路
本题考察的是对服务器任务的调度分配问题
通过分析,我们可以发现只要维护两个优先队列即可,一个存放正在运行的服务器,一个存放当前处于空闲状态的服务器。然后分别给这两个队列的加入定义规则:
(1)正在运行的服务器队列存放的有两个值,一个表示服务器的下标idx,另一个表示该服务器当前做的任务要持续到的时间点
要满足的条件:按时间升序排序
(2)处于空闲的服务器队列存放的也有两个值,一个表示服务器的权值,另一个表示该服务器的下标idx。
要满足的条件:先按权重升序排序,再按下标升序排序
这样一来,我们就可以来模拟多服务器执行多任务的调度分配问题了。通过一层while循环来枚举任务,当任务已经全部执行完毕,则程序结束,并返回res数组。那么while循环里面的步骤:
1. 先判正在运行的服务器队列中是否有(其实一开始是没有的),如果有,则查看一下处于队首的服务器有没有完成之前分配的任务(即判断当前服务器的完成时间点是否<=time),若已完成,就将该服务器取出,然后添加到空闲服务器队列中
2. 然后判断处于空闲的服务器队列中有没有服务器,有再继续检查当前的任务下标idx是否<=time,因为题目说第 j 项任务在第 j 秒可以开始处理,所以这里可以等于,且当前还有任务的话,就将new 一个服务器{服务器下标,持续到的时间点}加入到正在运行的服务器队列中去,不要忘了此时最重要的就是,将当前服务器的下标idx作为res[j]的值(也就是最终要返回的答案数组中的一个值),最后不要忘了idx++;
3. 最后这一步也是程序能否通过的一个关键步——即优化步骤:此时我们需要查看一下有没有处于空闲的服务器,如果没有,意味着中间会有一些时间是没有多余的空闲服务器来处理任务的,所以我们选择直接将时间直接跳到最早完成任务的那个服务器工作完时的时间(即将正在运行的服务器队列中队首的服务器下一次执行完的时间赋值给time),反之,则time++。
AC代码
1 class Solution { 2 public int[] assignTasks(int[] servers, int[] tasks) { 3 int n = tasks.length; 4 int m = servers.length; 5 int[] ans = new int[n]; 6 7 // 只需维护两个队列即可:一个正在运行的服务器队列,一个处于空闲状态的队列 8 9 //第一个表示下标 第二个表示还要多久 10 PriorityQueue<int[]> runnable = new PriorityQueue<>(new Comparator<int[]>() { 11 12 @Override 13 public int compare(int[] o1, int[] o2) { 14 // TODO Auto-generated method stub 15 return o1[1]-o2[1]; 16 } 17 }); //存放运行得 18 19 //第一个表示权值 第二个表示索引 20 PriorityQueue<int[]> free = new PriorityQueue<>(new Comparator<int[]>() { 21 22 @Override 23 public int compare(int[] o1, int[] o2) { 24 // TODO Auto-generated method stub 25 if(o1[0]>o2[0]){ 26 return 1; 27 }else if(o1[0]==o2[0]){ 28 if(o1[1]>o2[1]){ 29 return 1; 30 }else if(o1[1]==o2[1]){ 31 return 0; 32 }else{ 33 return -1; 34 } 35 }else{ 36 return -1; 37 } 38 } 39 }); 40 for(int i = 0;i<m;i++){ 41 free.add(new int[]{servers[i],i}); 42 } 43 int idx = 0; 44 int time = 0; 45 while(idx<n){ 46 // 正在运行的服务器队列,要知道,一开始是没有正在运行的服务器的 47 while(!runnable.isEmpty() && runnable.peek()[1]<=time){ 48 int[] top = runnable.poll(); 49 // 加入空闲服务器队列 50 free.add(new int[]{servers[top[0]],top[0]}); 51 } 52 while(!free.isEmpty() && idx<=time && idx<n){ 53 int[] p = free.poll(); 54 ans[idx] = p[1]; 55 // 加入任务 56 runnable.add(new int[]{p[1],tasks[idx]+time}); 57 idx++; 58 } 59 // 优化点 60 if(free.isEmpty()){ 61 // 如果当前没有空闲的服务器,那就将时间直接跳到最早完成任务的那个服务器工作完时的时间 62 time = runnable.peek()[1]; 63 }else{ 64 time++; 65 } 66 } 67 return ans; 68 } 69 }