产品经理(PM)有很多好的idea,而这些idea需要程序员实现。现在有N个PM,在某个时间会想出一个 idea,每个 idea 有提出时间、所需时间和优先等级。对于一个PM来说,最想实现的idea首先考虑优先等级高的(优先度数字越小越高),相同的情况下优先所需时间最小的,还相同的情况下选择最早想出的,没有 PM 会在同一时刻提出两个 idea。
同时有M个程序员,每个程序员空闲的时候就会查看每个PM尚未执行并且最想完成的一个idea,然后从中挑选出所需时间最小的一个idea独立实现,如果所需时间相同则选择PM序号最小的。直到完成了idea才会重复上述操作。如果有多个同时处于空闲状态的程序员,那么他们会依次进行查看idea的操作。
求每个idea实现的时间。
输入第一行三个数N、M、P,分别表示有N个PM,M个程序员,P个idea。随后有P行,每行有4个数字,分别是PM序号、提出时间、优先等级和所需时间。输出P行,分别表示每个idea实现的时间点。
输入描述:
输入第一行三个数N、M、P,分别表示有N个PM,M个程序员,P个idea。
随后有P行,每行有4个数字,分别是PM序号、提出时间、优先等级和所需时间。
全部数据范围 [1, 3000]。
输出描述:
输出P行,分别表示每个idea实现的时间点。
示例:
输入例子1:
2 2 5
1 1 1 2
1 2 1 1
1 3 2 2
2 1 1 2
2 3 5 5
输出例子1:
3
4
5
3
9
解析:
这到题要考虑的陷阱有点多:
1、这里存在两次显示的排序,一个是pm对自身的想法的排序,一个是程序员对pm们给的idea的一个排序(如程序员不会考虑idea的优先等级等,“最想要”应该是已经被pm提前抽象化了。),不能两者同时排序
2、当前程序员有空但新的idea还没有提出来。因此程序员应当对当前已经提出的idea排序
示例解析:
idea的提出时间表:
在时间点1,pm们提出了idea1,和idea4
此时两个程序员刚好完成,因此idea1和idea4的完成时间都是1+2=3
在时间点2,pm1提出idea2
但由于两个程序员在忙着idea1和idea4,所以占时被搁置,此时pm1的优先队列有一个元素
在时间点3,pm们提出idea3和idea5
此时,pm1的优先队列有2个元素,按照排序,先按优先等级大的排序,idea3是他目前最想做的,因此被加入到程序员的队列中,而pm2只有一个元素,因此直接加入
然后,程序员开始对队列排序,程序员1选择了耗费时间少的idea3,因此idea3的完成时间是3+2=5
此时,程序员2再次对队列排序,他挑选每个PM尚未执行并且最想完成的一个idea,即pm1的idea2被加入到程序员的队列中,显然,idea2耗费时间比idea5的要少,因此程序员2选择了idea2,因此idea2的完成时间为3+1=4
在时间点4
此时程序员2刚做完idea2,因此他被塞了idea5,因此idea5的完成时间为4+5=9
代码:
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
struct Idea
{
int id; //序号,用于输出问题
int pm; //pm序号
int time; //提出时间
int level; //优先等级
int useTime;//花费时间
Idea(int id, int pm, int time, int level, int useTime) :
id(id), pm(pm), time(time), level(level), useTime(useTime) {}
};
//仿函数
//pm对idea的排序
//注意,在使用优先队列时,优先队列使用的是堆排序,这使第三参数的效果与sort相反
struct cmp_pm
{
bool operator()(const Idea& a,const Idea& b)
{
//先按优先等级降序
if(a.level!=b.level)
{
return a.level < b.level;
}
//再按所需时间升序
else if(a.useTime!=b.useTime)
{
return a.useTime > b.useTime;
}
//再按提出时间升序
else
{
return a.time > b.time;
}
}
};
//程序员对idea的排序
struct cmp_pro
{
bool operator()(const Idea&a,const Idea&b)
{
//先按所需时间最少的升序
if(a.useTime!=b.useTime)
{
return a.useTime > b.useTime;
}
//再按pm号升序
else
{
return a.pm > b.pm;
}
}
};
//按时间排序,用于判断当前时间点
bool cmp_time(const Idea&a,const Idea&b)
{
return a.time < b.time;
};
int main()
{
int N, M, P;//pm,程序员,idea
cin >> N >> M >> P;
//读入idea数组
vector<Idea> ideas;
int pm, time, level, useTime;
for(int i=0;i<P;i++)
{
cin >> pm >> time >> level >> useTime;
ideas.emplace_back(Idea(i, pm - 1, time, level, useTime));
}
//记录idea结束时间
vector<int> ans(P);
//记录程序员剩余工作时间,只有在对应剩余时间为0才能添加新的idea任务
vector<int> pro(M, 0);
//N个优先队列,用于存储 当前时间点前所有未执行的idea ,并对他们排序
priority_queue<Idea, vector<Idea>, cmp_pm> cur_ideas[2005];
//先按提出时间排序,将提出的idea逐个加入程序员的排序中
sort(ideas.begin(), ideas.end(), cmp_time);
int count = 0;//idea已完成数
int p = 0; //idea编号
time = 1; //当前时间点
while (count<P)
{
//当队列中的idea的提出时间小于等于当前时间,则可加入到 每个pm当前最想做的idea的排序中
while (p<P&&ideas[p].time<=time)
{
cur_ideas[ideas[p].pm].push(ideas[p]);
p++;
}
//将每个pm当前最想做的idea加入到程序员的工作队列中,让程序员排序选择
//程序员的工作队列
priority_queue<Idea, vector<Idea>, cmp_pro> q;
for(int i=0;i<N;i++)
{
//注意判断当前pm队列是否为空
if(!cur_ideas[i].empty())
{
//由于当前的idea不一定被做,所以不着急先pop
q.push((cur_ideas[i].top()));
}
}
//更新程序员工作时间表
for(int i=0;i<M;i++)
{
//时间-1
if(pro[i]>0)
{
pro[i]--;
}
//如果当前程序员空闲,即时间为0,在idea队列不为空的情况下加入新的工作
if(pro[i]==0&&!q.empty())
{
//获取当前最优先idea并弹出
Idea idea = q.top();
q.pop();
//同时需弹出对应的pm序列,以更新pm当前最想做的idea
cur_ideas[idea.pm].pop();
//当当前idea被执行后,需加入它对应pm的次优先idea加入队列中,
//因为这个次优先的idea可能比其他idea优先 等级要高
//保证程序员队列中任何pm的idea都有
if(!cur_ideas[idea.pm].empty())
{
q.push(cur_ideas[idea.pm].top());
}
//更新程序员的工作时间表
pro[i] = idea.useTime;
//记录这个idea的完成时间
ans[idea.id] = time + idea.useTime;
//更新事件完成数量
count++;
}
}
//完成一批idea后记得更新时间
time++;
}
//最后按结束时间输出即可
for(int i=0;i<P;i++)
{
cout << ans[i] << endl;
}
}