字节题--多任务分配

编程题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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值