1014 Waiting in Line (30)(30 分)

该博客介绍了如何模拟银行排队服务系统,包括多个窗口、最大容纳人数限制和顾客处理时间。作者通过解决PAT(1014)问题阐述了具体的解题思路,涉及到复合队列的实现,以及如何处理顾客选择最短队伍的规则。文章提供了AC代码,并在最后进行了总结,强调了细节的重要性以及编程中的常见错误陷阱。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



1 题目

Suppose a bank has N windows open for service. There is a yellow line in front of the windows which devides the waiting area into two parts. The rules for the customers to wait in line are:

The space inside the yellow line in front of each window is enough to contain a line with M customers. Hence when all the N lines are full, all the customers after (and including) the (NM+1)st one will have to wait in a line behind the yellow line.

Each customer will choose the shortest line to wait in when crossing the yellow line. If there are two or more lines with the same length, the customer will always choose the window with the smallest number.

Customer[i] will take T[i] minutes to have his/her transaction processed.

The first N customers are assumed to be served at 8:00am.

Now given the processing time of each customer, you are supposed to tell the exact time at which a customer has his/her business done.

For example, suppose that a bank has 2 windows and each window may have 2 customers waiting inside the yellow line. There are 5 customers waiting with transactions taking 1, 2, 6, 4 and 3 minutes, respectively. At 08:00 in the morning, customer~1~ is served at window~1~ while customer~2~ is served at window~2~. Customer~3~ will wait in front of window~1~ and customer~4~ will wait in front of window~2~. Customer~5~ will wait behind the yellow line.

At 08:01, customer~1~ is done and customer~5~ enters the line in front of window~1~ since that line seems shorter now. Customer~2~ will leave at 08:02, customer~4~ at 08:06, customer~3~ at 08:07, and finally customer~5~ at 08:10.

Input

Each input file contains one test case. Each case starts with a line containing 4 positive integers: N (<=20, number of windows), M (<=10, the maximum capacity of each line inside the yellow line), K (<=1000, number of customers), and Q (<=1000, number of customer queries).

The next line contains K positive integers, which are the processing time of the K customers.

The last line contains Q positive integers, which represent the customers who are asking about the time they can have their transactions done. The customers are numbered from 1 to K.

Output

For each of the Q customers, print in one line the time at which his/her transaction is finished, in the format HH:MM where HH is in [08, 17] and MM is in [00, 59]. 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.

Sample Input

2 2 7 5
1 2 6 4 3 534 2
3 4 5 6 7

Sample Output

08:07
08:06
08:10
17:00
Sorry

2 解题思路

  这其实是个复合类型队列的问题,但是逻辑比较复杂,最开始的时候自己写,怎么都写不对,最后参考了一下柳婼大神的代码,发现自己忽略了很多细节,或者说,这道题考察的就是细节吧。其实能把题目附带的条件都想清楚了,倒也不难。
  题目大意是,银行里面有N个窗口,每个窗口能够排M个人,每个人都有自己要办的业务,每个业务要花费一定的时间,总共有K个人要办理业务,要注意,银行上班时间是8点~17点,如果超过了这个时间,就无法办理相关的业务。有Q次查询,查询自己办理业务结束时的时间,如果不在上班时间内,输出Sorry。
  大体思路:
  每个窗口可以看做一个队列,每个顾客可以看做一个元素,每个顾客的办理时间是已知的,这就可以计算每个队列的每个元素的出入队时间点以及队列最大的容量(顾客数<M和总办理时间<17点),然后根据条件进行判断每一个元素,是否超过下班时间,由于还要记录每个顾客的等待时间,所以还需要一个额外的布尔型数组记录是否超过下班时间。
  根据以上约束条件,,我们用一个vector来装填这个队列,为了便于计算,我们设置一个Line结构体来记录这个队列的首元素出队(用于满队的时候判断下一个结点何时入队)和最后一个元素的出队时间(判断是否超过了17点),以及结构体中包含一个queue 来记录每个顾客的流动情况和办理业务所需时间,便于计算和更新首尾元素的出队时间 ,然后把全部K个顾客从8点上班开始,逐一分配一下:
  如果队列不满,就按顺序输入顾客即可,但要注意的是,顾客入队的依据是哪队人最少,而不是排队时间最少。我们作为写这个分配程序的人,拥有上帝视角,知道每个顾客的办理时间,以及哪个队列时间最短,但按照常理,顾客显然不知道前面的人办理业务需要多长时间,所以入队的依据只能是哪队人最少就入哪队。所以队不满的情况下,按顺序从1到N窗口输入顾客即可。同时要记录整个队列的首元素出队的时间(即队首元素办理业务结束的时间),和最后一个元素的出队时间(即全部排队人的办理业务的时间累积总和)。
  如果满队的情况,遍历所有的窗口的记录的首元素出队时间,看哪个先出队,就进入哪个队列,但是要同时判断,是否超过17点的下班时间。找到最先出队的顾客,即我们入队的目标。
  每输入一个顾客,都要记录他的结束时间,如果时间超过了下班时间,那么还需要标记一下。进行判断的时候,同意换算成分钟,这样可以避免时间格式的转换过程。

3 AC代码

/*
** @Brief:No.1014 of PAT advanced level.
** @Author:Jason.Lee
** @Date:2018-7-13 
*/

#include<iostream>
#include<queue>
#include<vector>
#define DEAD_LINE 540 

using namespace std;

struct Line{
    int poptime;// 记录队头出队时间 
    int endtime;// 记录队尾出队时间 
    queue<int> q;// 记录中间元素排队时间 
};
int main(){
    int N,M,K,Q;
    while(scanf("%d%d%d%d",&N,&M,&K,&Q)!=EOF){
        int index = 1;
        vector<int> time(K+1);// 记录每一个顾客办理业务所需要的时间 
        vector<int> result(K+1);// 计算每一个顾客业务办理结束时间的结果 
        // 输入办理业务消耗时间 
        for(int i=1;i<=K;i++){
            scanf("%d",&time[i]);
        } 
        vector<Line> windows(N+1);// 每个窗口看做是一个队列 
        vector<bool> sorry(K+1,false);// 每个顾客都保存一个记录 
        // 为每个窗口安排顾客 ,从1到N,每个最多M个 
        for(int i=1;i<=M;i++){
            for(int j=1;j<=N;j++){
                if(index<=K){
                    // 第index顾客进入第j个窗口 
                    windows[j].q.push(time[index]);
                    if(windows[j].endtime>=DEAD_LINE){
                        sorry[index]=true;
                    }
                    // 第j个窗口的第i个顾客,出队时间等于上一个顾客的出队时间+办理业务的时间 
                    windows[j].endtime += time[index];
                    // 对于第一个顾客的情况,出队时间,即为队列最终元素结束时间 
                    if(i==1){
                        windows[j].poptime = windows[j].endtime;
                    }
                    // 第index个顾客的等待结果,当然,这个结果有可能是“超时”的,超时的index会对应的在sorry数组中记录 
                    result[index] = windows[j].endtime;
                    index++;
                }
            }// for-j
        }//for-i

        // 对于顾客总数超出了 N*M的情况 
        while(index<=K){
            // 当队列都满的时候,从第一个窗口开始查询最早的出队时间 
            int temp_min = windows[1].poptime;
            int temp_window = 1;
            // 遍历窗口2~N,得到最早出队的窗口 temp_window 
            for(int i=2;i<=N;i++){
                if(windows[i].poptime<temp_min){
                    temp_window = i;
                    temp_min = windows[i].poptime;
                }
            }//for-i

            windows[temp_window].q.pop();//出队 
            windows[temp_window].q.push(time[index]);// 入队 
            windows[temp_window].poptime += windows[temp_window].q.front(); // 更新队首元素的出队时间 
            if(windows[temp_window].endtime>=DEAD_LINE){
                sorry[index] = true;// 如果最早出队的窗口都超出了下班时间,那么无法办理业务,输出sorry 
            }
            windows[temp_window].endtime +=time[index];// 但还是要更新最后一个元素的出队时间 
            result[index] = windows[temp_window].endtime;
            index++;
        }//while-end

        for(int i = 1;i<=Q;i++){
            int query = 0;
            int minute = 0;
            scanf("%d",&query);
            minute = result[query];
            if(sorry[query]==true){
                printf("Sorry\n");
            }else{
                printf("%02d:%02d\n",(minute+480)/60,minute%60); 
            }
        }//for-i        
    }// while-end 
    return 0;
} 

4 总结

  
  一个小插曲,原本提交代码的时候,把某处的+=写成了=+,结果最后一个测试点一直不过,当时已然昏天暗地、头昏脑涨,愣是没有看出来。从经验上讲,一个测试点不过,一般是边界问题,结果最后在程序逻辑上分析了老半天,无果,又对照了大神的代码查了半天,发现一个令人无语的bug。如果全不通过或许还能意识到是语法写错了,一个测试点不通过反而带来了误导。记录一下,警示自己。
  善用STL会极大的提高编程效率,但是同时要注意STL的时间成本会略高一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值