这道题其实思路还是挺清晰的,用一个结构体记录顾客到达时间begin及需要处理的时间长度span,存到结构体数组records中,到达时间用string类型存储,调用sort函数依据字典序排序。(也可以转化为都计算相对"00:00:00"多少秒,这样也能方便比较大小,计算等待时间)
对于抵达时间超过17:00:01的顾客直接忽略;
模拟排队的过程,根据先来后到的原则,先来的顾客会去先完成业务的窗口,可以维持一个窗口的优先队列,也是存储string类型的时间,表示该时刻时才能接待业务,初始化因为银行8点上班,所以K个窗口时间初始均为“08:00:00”;
对于已经排好序后的records,从小到大依次处理每一个顾客,拿该顾客的到达时间与队列中的top()元素时间(也就是最早可以接待顾客的时间)进行比较,顾客到达时间小于队列中的最早时间的话则需要计算等待时间,反之不需要等待直接进行处理,如07:55:00到的顾客需要等“08:00:00”的窗口5min,而“08:30:00”到的顾客不需要等待"08:15:00"就已经ready for顾客的窗口。
计算等待时间以秒为单位,最后统一化为分钟,算法就是不断累加秒,该进位进位,直到两时间相等。
为了思考的方便,写了两个函数count和update分别用于计算等待时间和为更新为该顾客服务的窗口下一个服务的时间,这俩函数可以合二为一。
需要注意的是,更新时间的时候,同样需要比较顾客的到达时间与队列中的top()元素时间,如果顾客没有等待,说明顾客来得晚,如“08:30:00”到的顾客不需要等待"08:15:00"就绪的窗口,但是更新时间要用顾客的到达时间;如果顾客等待了,如07:55:00到的顾客需要等“08:00:00”的窗口5min,更新时间就要同窗口的时间!
如果一开始思维产生漏洞,一直用队列中的top()元素时间进行更新,则能拿18分(用时34min),测试点345都过不去,如果一直用顾客到达时间更新,则能拿个位数的分数,测试点0345都过不去qwq。
所以写这种模拟题还是按照步骤一步一步来,不要跳步,该先排序就排好序再处理后面的队列,上述思维漏洞感觉是因为等待时间和更新时间分开考虑导致的,如果一块考虑的话对是否等待进行讨论的时候可能就会意识到更新的时候的时间基点也会依据是否等待而不同,总之一开始没想到,后面突然灵光乍现,经验还需积累。
注意:如果为了图省事#define int long long了,要记得如果用到scanf,要把里面的%d换为%lld!!
·
·
·
可以过pat的完整代码:(牛客有一例子超时)
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct Record{
string begin;
int span;
Record(){
}
Record(string _begin,int _span){
begin=_begin;
span=_span;
}
void show(){
cout << begin << " " << span << endl;
}
}records[10005];
bool cmp(struct Record a,struct Record b){
return a.begin<b.begin;
}
priority_queue<string,vector<string>,greater<string>> q;
void update(struct Record customer,priority_queue<string,vector<string>,greater<string> >&q);
int count(string cus,string window);
int main(){
int K,N;
cin >> N >> K;
int cnt=0;
for(int i=0;i<N;i++){
string begin;
int span;
cin >> begin >> span;
if(begin<="17:00:00") cnt++;
records[i]=Record(begin,span);
//records[i].show();
}
sort(records,records+N,cmp);//对来的人先进行排序
for(int i=0;i<K;i++){//初始化K个窗口
q.push("08:00:00");
}
int wait=0;//s为单位
for(int i=0;i<cnt;i++){//对有效数据进行统计,以秒计总数,最后要记得转为分钟
wait+=count(records[i].begin,q.top());//计算需要的时长
update(records[i],q);//更新窗口时间
}
printf("%.1f",(double)(wait*1.0/60/cnt));
return 0;
}
int count(string cus,string window){//返回多少秒
if(window<=cus) return 0;
//if(window>="17:00:00") window="17:00:00";
int h1=(window[0]-'0')*10+window[1]-'0';
int m1=(window[3]-'0')*10+window[4]-'0';
int s1=(window[6]-'0')*10+window[7]-'0';
int h2=(cus[0]-'0')*10+cus[1]-'0';
int m2=(cus[3]-'0')*10+cus[4]-'0';
int s2=(cus[6]-'0')*10+cus[7]-'0';
int res=0;
while(h2<h1 || m2<m1 || s2<s1){
s2++;
res++;
if(s2==60){
s2-=60;
m2++;
}
if(m2==60){
m2-=60;
h2++;
}
}
// cout << res << endl;
return res;
}
void update(struct Record customer,priority_queue<string,vector<string>,greater<string> >&q){
//取决于顾客有没有等待,没等待用当前顾客的时间+他的span,等待了采用q.top的时间
string time;
if(customer.begin>=q.top())//没有等待
time=customer.begin;
else time=q.top();//顾客等待了
int span=customer.span;
//在time上加上span分钟
int h=(time[0]-'0')*10+time[1]-'0';
int m=(time[3]-'0')*10+time[4]-'0';
m+=span;
while(m>=60){
m-=60;
h++;
}
time[0]=h/10+'0';
time[1]=h%10+'0';
time[3]=m/10+'0';
time[4]=m%10+'0';
q.pop();
q.push(time);
}
上述代码牛客网上下例会超时:
还以为是cin很多次的问题,原来是N足够大时计算等待时间用秒累加太慢的缘故,加上前面的优化(注释部分)就能通过:
这说明这个优化还是很必要的,计算日期差值的时候也要注意,当然这里计算等待时间,还可以用都相对于00:00:00的秒数作差,因为和phone bills需要每小时处理不同,这里只需要算总时间,因此方法要灵活运用。
·
·
·
最终代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct Record{
string begin;
int span;
Record(){
}
Record(string _begin,int _span){
begin=_begin;
span=_span;
}
void show(){
cout << begin << " " << span << endl;
}
}records[10005];
bool cmp(struct Record a,struct Record b){
return a.begin<b.begin;
}
priority_queue<string,vector<string>,greater<string>> q;
void update(struct Record customer,priority_queue<string,vector<string>,greater<string> >&q);
int count(string cus,string window);
signed main(){
int K,N;
cin >> N >> K;
int cnt=0;
for(int i=0;i<N;i++){
char arrive[9];
int span;
//scanf("%s %lld",arrive,&span);
string begin=string(arrive);
cin >> begin >> span;
if(begin<="17:00:00") cnt++;
records[i]=Record(begin,span);
//records[i].show();
}
sort(records,records+N,cmp);//对来的人先进行排序
for(int i=0;i<K;i++){//初始化K个窗口
q.push("08:00:00");
}
int wait=0;//s为单位
for(int i=0;i<cnt;i++){//对有效数据进行统计,以秒计总数,最后要记得转为分钟
wait+=count(records[i].begin,q.top());//计算需要的时长
update(records[i],q);//更新窗口时间
}
printf("%.1f",(double)(wait*1.0/60/cnt));
return 0;
}
int count(string cus,string window){//返回多少秒
if(window<=cus) return 0;
//if(window>="17:00:00") window="17:00:00";
int h1=(window[0]-'0')*10+window[1]-'0';
int m1=(window[3]-'0')*10+window[4]-'0';
int s1=(window[6]-'0')*10+window[7]-'0';
int h2=(cus[0]-'0')*10+cus[1]-'0';
int m2=(cus[3]-'0')*10+cus[4]-'0';
int s2=(cus[6]-'0')*10+cus[7]-'0';
int res=0;
while(h2<h1-1){
h2++;
res+=60*60;
}
while(m2<m1-1){
m2++;
res+=60;
}
while(h2<h1 || m2<m1 || s2<s1){
s2++;
res++;
if(s2==60){
s2-=60;
m2++;
}
if(m2==60){
m2-=60;
h2++;
}
}
// cout << res << endl;
return res;
}
void update(struct Record customer,priority_queue<string,vector<string>,greater<string> >&q){
//取决于顾客有没有等待,没等待用当前顾客的时间+他的span,等待了采用q.top的时间
string time;
if(customer.begin>=q.top())//没有等待
time=customer.begin;
else time=q.top();//顾客等待了
int span=customer.span;
//在time上加上span分钟
int h=(time[0]-'0')*10+time[1]-'0';
int m=(time[3]-'0')*10+time[4]-'0';
m+=span;
while(m>=60){
m-=60;
h++;
}
time[0]=h/10+'0';
time[1]=h%10+'0';
time[3]=m/10+'0';
time[4]=m%10+'0';
q.pop();
q.push(time);
}