题意:
-
银行排队办理业务,银行有N个窗口,每个窗口前排有队伍
-
每条队伍人数给出限制M人,即题中描述黄线,黄线前是队伍,黄线后是还未决定排那条队的客人
-
所有顾客都是在08:00同时到达银行准备办理业务
-
要求求出顾客结束办理业务的时间
-
题中有一个陷阱,即判断“Sorry”
的顾客是17:00前还没有开始接受服务的客人,只要客人17:00前开始了办理业务,业务就可以办理完。这一点理解错的话,测试点2,4,5过不去
解题思路
- 黄线内的顾客容量,即M×N,是到了即可以进去排队的,所以可以直接加入每个窗口前的队伍中,所以代码中第一个循环是处理可以立即去排队的客人。这里写代码有一个技巧是,没有必要具体去算循环的每一轮已经排了多少个人(N*i+j),然后比较看是否要跳出循环,这样比较麻烦还容易出错;用一个
if(index<=k)
就可以处理了。 - 每一位客人的业务处理结束时间是基于他队伍前一位顾客的结束时间计算的,所以把第一行顾客的结束时间显式地计算出来,后面的顾客就可以递推地进行了(此处“递推”是语文含义不是计算机含义)
- 然后开始一位一位处理黄线外的客人,遍历到每一位的时候,扫一遍所有窗口的队列头部,找到最早结束的顾客,把他pop出去,然后进入这个队列(这里就体现了所有顾客最初时刻同时到达使这道题目简易的很多的特点,任何一个窗口队列,少一个人,就可以补一个人),递推地计算结束时间
#include <iostream>
#include <queue>
#include <queue>
using namespace std;
struct Customer{
int begin;
int done;
int lasting;
};
queue<int> windows[21];
Customer cus[1005];
int main(){
int n,m,k,q;
int index=1;
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int i=1;i<=k;i++){
int tmplas;
scanf("%d",&tmplas);
cus[i].lasting = tmplas;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(index<=k){
if(index<=n) {windows[j].push(index);cus[index].begin=0;cus[index].done=0+cus[index].lasting;}
else{
int finish = cus[windows[j].back()].done;
cus[index].done = finish+cus[index].lasting;
windows[j].push(index);
}
index++;
}
}
}
while(index<=k){
int earlist=600,earlistIndex=0;
for(int i=1;i<=n;i++){
if(cus[windows[i].front()].done<earlist) {earlist=cus[windows[i].front()].done;earlistIndex = i;}
}
int finish = cus[windows[earlistIndex].back()].done;
windows[earlistIndex].pop();
cus[index].done = finish+cus[index].lasting;
windows[earlistIndex].push(index);
index++;
}
for(int i=0;i<q;i++){
int id;
scanf("%d",&id);
if(cus[id].done-cus[id].lasting>=540) printf("Sorry\n");
else printf("%02d:%02d\n",cus[id].done/60+8,cus[id].done%60);
}
return 0;
}
附:
-
我写完这道题看了看大家的解法,觉得自己的解法还是相对简洁,所以贴出来给大家作一个参考,希望对大家有帮助~
-
另外就是我也有一个问题,我一开始写队列数组用的是
vector<queue<int> > windows
, vscode会报‘bad alloc’,后换成queue<int> windows[N]
就正常跑了,不知道大家怎么看~