PAT甲级排队问题合集 (持续更新中)
Posted CSU迦叶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT甲级排队问题合集 (持续更新中)相关的知识,希望对你有一定的参考价值。
已加入的习题
A1014,A1017
问题1和2共性
1. 都是排队问题
2. 都有一条黄线
3. 都需要找到最先离开人的队伍
4. 都有着服务时间段限制(迟于某个时间点来不予受理)
问题1:1014 Waiting in Line
问题链接:1014 Waiting in Line
这一题,所有的顾客一起来了,但是有个先后顺序。不会出现哪条队伍在某段时间没有处理任何事务的情况。
考虑到队伍有着先进先出的特点,所以用queue<int>来模拟。
最后要求出给定编号的顾客被处理结束的时间点。该问题可以转化为求给定编号的顾客问题得到处理的总时长,包括等待时间+自身被处理时间。分别存放在数组wt和pt中,其中pt直接读入,wt则需要在确定顾客所在队伍的编号,wt等于所在队伍该顾客前所有顾客的处理时间之和。这里采用一个单独的数组winTime[],存放每个队伍已经入队的顾客处理时间之和。
现在重点解决确定每个顾客入哪个队的问题。分两种情况讨论。
1.顾客一来就可以进入黄线内。也就是编号在1~窗口数x每条队伍黄线内最多人数范围内的顾客。
他们的编号很好确定,直接用顾客编号对窗口数取余。
2.顾客并非一来就可以进入黄线内,而是要等到某一支队伍离开一个人。
问题转化为找到最先离开人的队伍。
引入数组leftTime存放每个窗口已经离开的顾客的处理时间之和,如果一支队伍的leftTime和队伍第一人的处理时间之和最小,那它就是我要找的。
易错点:
对于这句话的理解:Note that since the bank is closed everyday after 17:00, for those customers who cannot be served before 17:00, you must output Sorry
instead.
到底什么样的人对他说抱歉?不是17:00之前还没能处理完的人,而是,在17:00之前没能来的人,17:00卡点来也要说抱歉啦。
问题2:1017 Queueing at Bank
这题所有顾客并不是一起来的,而是有着明确的抵达时间点,我们可以想象一种秒数时间制,把当天的00:00:00记为0,则开门时间和关门时间分别为
const int earliest = 8*60*60;
const int latest = 17*60*60;
和上一题相同,都是17点后(包括17点)来的人,不予受理,但是,如果一个人在十七点来,哪怕最短的队伍还要处理到17点半,这个人也会在17点半后被接着处理, 可以理解成17点后,大门紧闭,但是已经进去的人加班也会被处理完。
本题要求,所有会被处理的人的等待时间的平均值,问题转化为求等待时间的总值,包括两部分:所有人等银行开门的时间+等前面的人被处理完的时间。
本题由于不是无缝对接(可能存在某个时间段某个队伍没有处理任何顾客)所有不再记录每条队伍中顾客被处理的总时间,而是能够处理下一个顾客的最早时间点。我是用数组win。win在一开始被初始化为8点的秒数时间制,每当有新的顾客加入队伍就更新一次。
用一个结构体cst来存放顾客的信息:
struct cst{
int trueArrSec = 0;//真实的秒数制抵达时间(用于排序)
int arrSec = 0;//(矫正后)秒数制的抵达时间
int pm = 0;//process minute 事件处理的分钟数
};
一开始我是没有trueArrSec这个属性的,直到发现需要使用这个属性来排序。。。
用结构体数组来存放待处理顾客队列
vector<cst> vi;//待处理顾客队列
根据到达时间点对顾客排序,然后每个顾客都判断当前哪支队伍先离开人。
在这个过程中,1是要更新队伍能处理下一个顾客的时间点,2是要增加等待时间的总值,也就是出现顾客抵达的时间点小于当前队伍能处理下一个顾客的时间点,要把这个时间差加上。
tws += max(0,wSec-vi[i].arrSec);//类型2
win[wNo] = max(vi[i].arrSec,win[wNo]) + 60*vi[i].pm;//更新当前窗口需要等待到多少秒
易错点:开始我有一个测试点是答案错误的,后来找到原因,原来在寻找最先离开人的队伍时,我把最早要等待到的时间点初始化的值不够大,后来改成10的9次方,那个用例就通过了。
问题1 AC代码
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
const int sorryTime = 9*60;
void print(int wt,int pt){
if(wt==-1){
printf("Sorry\\n");
return;
}
int totalTime = wt+pt;
int h = totalTime/60;
int m = totalTime%60;
printf("%02d:%02d\\n",8+h,m);
return;
}
int pt[1001] = {0};//每个顾客的处理时间processing time,顾客编号从1开始
int wt[1001] = {0};//每个顾客的等待时间waiting time,顾客编号从1开始
int main(){
int winN;//窗口数量 1<=winN<=20
int capacity;//黄线内容量 1<=capacity<=10
int cusN;//顾客数量 1<=cusN<=1000
int QN;//查询的数量
scanf("%d %d %d %d",&winN,&capacity,&cusN,&QN);
for(int i=1;i<=cusN;i++){
scanf("%d",&pt[i]);
}
queue<int> win[20];//0号到20号窗口
int winTime[20] = {0};//每个窗口的总处理时间
//对于黄线内放得下的顾客
int inside = winN*capacity;//黄线内的容量
for(int i=1;i<=cusN&&i<=inside;i++){
int winNo = i%winN;//i号顾客将进入窗口winNo 0<=winNo<=winN-1
win[winNo].push(i);//进入窗口
if(winTime[winNo]>=sorryTime)wt[i] = -1;
else wt[i] = winTime[winNo];
winTime[winNo]+=pt[i];//当前窗口处理的总时间增加
}
int leftTime[20] = {0};//每个窗口已经离开的顾客花去的时间
//对于黄线外的顾客
for(int i=inside+1;i<=cusN;i++){
int quickNo;//最快的窗口编号
int quickWt = 1000000000;//最快的等待时间:反映哪个队伍最先缩短
int leaveNo;//要离开的兄弟的编号
for(int j=0;j<winN;j++){//筛选出最先缩短的队
int h = win[j].front();
if(pt[h]+leftTime[j]<quickWt){
quickWt = pt[h]+leftTime[j];
quickNo = j;
leaveNo = h;
}
}
//第i号顾客会在窗口quickNo的当前第一个顾客离开后加入队尾
win[quickNo].pop();//当前窗口第一个顾客离队
leftTime[quickNo] += pt[leaveNo];//当前窗口离开的顾客花去的时间增加
if(winTime[quickNo]>=sorryTime)wt[i] = -1;
else wt[i] = winTime[quickNo];
win[quickNo].push(i);//顾客i入队
winTime[quickNo]+=pt[i];//当前窗口处理的总时间增加
}
int no;
for(int i=0;i<QN;i++){
scanf("%d",&no);
print(wt[no],pt[no]);
}
return 0;
}
问题2 AC代码
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 1000000000;
const int earliest = 8*60*60;
const int latest = 17*60*60;
int chaToSec(int h,int m,int s){
return h*60*60+m*60+s;
}
struct cst{
int trueArrSec = 0;//真实的秒数制抵达时间(用于排序)
int arrSec = 0;//(矫正后)秒数制的抵达时间
int pm = 0;//process minute 事件处理的分钟数
};
vector<cst> vi;//待处理顾客队列
int win[100];//窗口要排队到XX秒
bool cmp(cst a,cst b){
return a.trueArrSec<b.trueArrSec;
}
int main(){
int winN,cusN;//窗口数量,顾客数量
scanf("%d %d",&cusN,&winN);
int tws = 0;//totalWaitSec 总体等待秒数 由所有待处理顾客的1.等银行开门 和 2.等待他人处理时间 组成
while(cusN--){
int h,m,s,pm;
scanf("%d:%d:%d %d",&h,&m,&s,&pm);
int arrSec = chaToSec(h,m,s);
if(arrSec<latest){//在17:00:00前来了,才会被加入待处理顾客队列
cst c;
c.trueArrSec = arrSec;
if(arrSec<earliest){//要是8:00前就跑来了
tws += (earliest-arrSec);//类型1
c.arrSec = earliest;
}
else c.arrSec = arrSec;
if(pm<=60)c.pm = pm;//每个顾客被处理的时间不超过60min
else c.pm = 60;
vi.push_back(c);//将这个顾客加入队列
}
}
sort(vi.begin(),vi.end(),cmp);//将顾客按照抵达的先后顺序排序
//初始化窗口的 当前要排队到多少秒
fill(win,win+winN,earliest);
for(int i=0;i<vi.size();i++){
int wSec = maxn;//至少等待到这一秒
int wNo = 0;//等待的窗口
for(int j=0;j<winN;j++){
if(win[j]<wSec){
wSec = win[j];
wNo = j;
}
}
tws += max(0,wSec-vi[i].arrSec);//类型2
win[wNo] = max(vi[i].arrSec,win[wNo]) + 60*vi[i].pm;//更新当前窗口需要等待到多少秒
}
printf("%.1f",tws/60.0/vi.size());
return 0;
}
以上是关于PAT甲级排队问题合集 (持续更新中)的主要内容,如果未能解决你的问题,请参考以下文章