编程题3_字节跳动2018校招大数据方向(第一批)_牛客网
这题的处理逻辑并不难,但是处理起来需要考虑的东西很多,实现起来是不容易的,需要维护的数组较多。
先明确题意:
-
对于pm来说,考虑的顺序是:优先级 > 所需时间 > 提出时间。
-
对于程序员来说,空闲的时候就会查看每个PM尚未执行并且最想完成的一个idea,考虑顺序是:所需时间 > pm序号。
注意:程序员每次查看的时候能看到的是每个pm最想完成的一个idea(,如果我们为每个pm维护一个优先级队列的话,那么程序员每次查看的时候只能看到队首的元素。
所以整体思路过程是:
- 初始时间cur_time == 0,此时所有的idea均在ideas数组(任务池)里;
- 随着时间推移(++cur_time),就需要进行数据的更新:
1、当轮到某些任务的时间片时(proposed_time <= cur_time),这些任务(idea)会被pm提出,那就将他们导入相应pm的已提出队列(有机会被程序员选中去实现)。所以我们需要为每一个pm维护一个已提出队列;
2、但是已提出的idea离被执行还有好几步,它首先需要被程序员选中(程序员只能选择每个已提出队列的队首元素),进入已就绪队列,然后在已就绪队列里面进行按照实现时间的排序,如果它排在已就绪队列的首部(实现时间最短),那么下一个空闲的程序员就会选择它执行。
3、那么我们还需要检查是否有程序员进入空闲状态,所以我们为程序员维护一个程序员状态数组,该数组表示某个程序员的当前idea还剩下多少执行时间。如果剩余执行时间为0,代表该程序员空闲。如果程序员空闲,就需要为该程序员进行任务分配。任务分配的过程是:选择已就绪队列的队首idea,然后由于该idea已进入执行状态,我们需要更新已就绪队列:加入 该idea 对应的pm 对应的已提出队列 的队首元素。
整体模拟的是一个随时间的轮转过程,退出循环的条件就是所有idea(p个)均被执行。由于题目要求的是每个idea的完成时间,所以还需要维护一个完成时间的数组,不过每个idea被程序员选中的时候,它的完成时间就已经确定下来了。
每个idea的历程是:未被提出(在ideas池子里)->提出,进入已提出队列->进入就绪队列->被选中执行。
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
vector<int> res;
class idea {
public:
int idx, pm_num, proposed_time, priority, time_needed;
idea(int idx, int pm_num, int proposed_time, int priority, int time_needed) : idx(idx), pm_num(pm_num),
proposed_time(proposed_time), priority(priority), time_needed(time_needed){}
};
struct cmp_pm{//pm的比较规则
bool operator()(const idea &item1, const idea &item2){
if(item1.priority == item2.priority)//优先级相等,所需时间升序,所需时间也相等,提出时间升序
return item1.time_needed == item2.time_needed ? item1.proposed_time > item2.proposed_time : item1.time_needed > item2.time_needed;
return item1.priority < item2.priority;//优先级降序
}
};
bool cmp_programmer(const idea &item1, const idea &item2){//程序员的额比较规则
if(item1.time_needed == item2.time_needed) return item1.pm_num < item2.pm_num;
return item1.time_needed < item2.time_needed;
}
void process(int n, int m, int p, vector<idea>& ideas){
res.resize(p);
int num_of_ideas_left = p;//当前尚未分配的任务
sort(ideas.begin(), ideas.end(), [](const idea &a, const idea &b){return a.proposed_time < b.proposed_time;});//先按照提出时间升序
vector<priority_queue<idea, vector<idea>, cmp_pm>> idea_proposed(n+1);//每个pm当前时间点已经提出的idea(已提出队列)
vector<int> time_left(m+1, 0);//记录每个程序员的当前任务的剩余处理时间
int cur_time = 0, last_idx = 0;//last_idx记录上一次添加到已提出队列的下标
while (num_of_ideas_left) {//还有任务没有处理完
++cur_time;//时间增加后,可能有idea被提出,需要从ideas移除(并不是真的移除)并加入对应pm的已提出队列
int idx = last_idx;
while (idx < ideas.size() && ideas[idx].proposed_time <= cur_time) ++idx;
for (int i = last_idx; i < idx; ++i) idea_proposed[ideas[i].pm_num].push(ideas[i]);//添加
last_idx = idx;
vector<idea> idea_ready;//已就绪队列(每次循环都会定义一次)
for (int i = 1; i <= n; ++i){//查看每个pm最想完成的idea(队首idea)
if (!idea_proposed[i].empty()) idea_ready.push_back(idea_proposed[i].top());
}
sort(idea_ready.begin(), idea_ready.end(), cmp_programmer);//按照程序员的准则排序
for(int i = 1; i <= m; ++i){//m个程序员
if (time_left[i] > 0) --time_left[i];
if(time_left[i] == 0 && !idea_ready.empty()){//程序员进入空闲状态且就绪队列非空
idea item = idea_ready[0];//取出队首idea
idea_ready.erase(idea_ready.begin());//更新就绪队列
time_left[i] = item.time_needed;//执行,更新该程序员剩余执行时间
idea_proposed[item.pm_num].pop();//在已提出队列里进行移除(因为就绪队列是每次循环都生成,所以只能在这进行移除)
//更新就绪队列(上面某pm最想完成的idea开始执行,现在把该pm的下一个最想完成的idea加入就绪队列)
if (!idea_proposed[item.pm_num].empty()) {
//之前的就绪队列就是有序的,采用二分插入
auto next_item = idea_proposed[item.pm_num].top();
auto it = lower_bound(idea_ready.begin(), idea_ready.end(), next_item, cmp_programmer);
idea_ready.insert(it, next_item);
}
res[item.idx-1] = cur_time + item.time_needed;//完成时间
--num_of_ideas_left;
}
}
}
}
int main() {
int n, m, p;
cin >> n >> m >> p;
vector<idea> ideas;
int pm_num, proposed_time, priority, time_needed;
for (int i = 0; i < p; i++) {
cin >> pm_num >> proposed_time >> priority >> time_needed;
ideas.push_back(idea(i + 1, pm_num, proposed_time, priority, time_needed)); //idea序号从1开始
}
process(n, m, p, ideas);
for (const auto &v : res) cout << v << endl;
return 0;
}
其实就绪队列可以再使用一个优先级队列而不是数组,方便插入和删除。不过需要注意优先级队列的比较规则,不要搞反
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
vector<int> res;
class idea {
public:
int idx, pm_num, proposed_time, priority, time_needed;
idea(int idx, int pm_num, int proposed_time, int priority, int time_needed) : idx(idx), pm_num(pm_num),
proposed_time(proposed_time), priority(priority), time_needed(time_needed){}
};
struct cmp_pm{//pm的比较规则
bool operator()(const idea &item1, const idea &item2){
if(item1.priority == item2.priority)//优先级相等,所需时间升序,所需时间也相等,提出时间升序
return item1.time_needed == item2.time_needed ? item1.proposed_time > item2.proposed_time : item1.time_needed > item2.time_needed;
return item1.priority < item2.priority;//优先级降序
}
};
struct cmp_programmer{
bool operator()(const idea &item1, const idea &item2){//程序员的额比较规则
if(item1.time_needed == item2.time_needed) return item1.pm_num > item2.pm_num;
return item1.time_needed > item2.time_needed;
}
};
void process(int n, int m, int p, vector<idea>& ideas){
res.resize(p);
int num_of_ideas_left = p;//当前尚未分配的任务
sort(ideas.begin(), ideas.end(), [](const idea &a, const idea &b){return a.proposed_time < b.proposed_time;});//先按照提出时间升序
vector<priority_queue<idea, vector<idea>, cmp_pm>> idea_proposed(n+1);//每个pm当前时间点已经提出的idea(已提出队列)
vector<int> time_left(m+1, 0);//记录每个程序员的当前任务的剩余处理时间
int cur_time = 0, last_idx = 0;//last_idx记录上一次添加到已提出队列的下标
while (num_of_ideas_left) {//还有任务没有处理完
++cur_time;//时间增加后,可能有idea被提出,需要从ideas移除(并不是真的移除)并加入对应pm的已提出队列
int idx = last_idx;
while (idx < ideas.size() && ideas[idx].proposed_time <= cur_time) ++idx;
for (int i = last_idx; i < idx; ++i) idea_proposed[ideas[i].pm_num].push(ideas[i]);//添加
last_idx = idx;
priority_queue<idea, vector<idea>, cmp_programmer> idea_ready;//已就绪队列(每次循环都会定义一次)
for (int i = 1; i <= n; ++i){//查看每个pm最想完成的idea(队首idea)
if (!idea_proposed[i].empty()) idea_ready.push(idea_proposed[i].top());
}
for(int i = 1; i <= m; ++i){//m个程序员
if (time_left[i] > 0) --time_left[i];
if(time_left[i] == 0 && !idea_ready.empty()){//程序员进入空闲状态且就绪队列非空
idea item = idea_ready.top();//取出队首idea
idea_ready.pop();//更新就绪队列
time_left[i] = item.time_needed;//执行,更新该程序员剩余执行时间
idea_proposed[item.pm_num].pop();//在已提出队列里进行移除(因为就绪队列是每次循环都生成,所以只能在这进行移除)
//更新就绪队列(上面某pm最想完成的idea开始执行,现在把该pm的下一个最想完成的idea加入就绪队列)
if (!idea_proposed[item.pm_num].empty()) {
auto next_item = idea_proposed[item.pm_num].top();
idea_ready.push(next_item);
}
res[item.idx-1] = cur_time + item.time_needed;//完成时间
--num_of_ideas_left;
}
}
}
}
int main() {
int n, m, p;
cin >> n >> m >> p;
vector<idea> ideas;
int pm_num, proposed_time, priority, time_needed;
for (int i = 0; i < p; i++) {
cin >> pm_num >> proposed_time >> priority >> time_needed;
ideas.push_back(idea(i + 1, pm_num, proposed_time, priority, time_needed)); //idea序号从1开始
}
process(n, m, p, ideas);
for (const auto &v : res) cout << v << endl;
return 0;
}