PAT-ADVANCED1014——Waiting in Line

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805498207911936

题目描述:

题目翻译:

1014 排队

假设一家银行共有N个窗口开放提供服务。每个窗口前面有一条黄线,将等待区域分成两部分。客户的等待规则如下:

        每个窗口前面黄线以内的区域足够容纳M个客户。因此,当N个窗口的该区域都满时,所有编号在(NM + 1)以及其之后的客户都必须等待在黄线后面。

        当进入黄线时,每个客户都会选择人数最少的队伍。如果有2条或2条以上人数一样的队伍,客户会选择窗口编号最小的窗口。

        客户i需要Ti时间处理他/她的业务。

        前N个客户将在08:00被服务。

现在给定每个客户的处理时间,你需要准确算出何时该客户的业务处理完毕。

举个例子,假设该银行有2个窗口且每个窗口黄线内可以容纳2人。有5个客户排队,其处理时间依次是1 2 6 4 3。在早上08:00,客户1在窗口1被服务,客户2在窗口2被服务。客户3会选择在窗口1等待,客户4会选择在窗口2等待。客户5会等待在黄线外。

在08:00,客户1的业务处理完毕,客户5进入窗口1的队伍,因为此时窗口1的队伍更短。客户2会在08:02离开,客户4会在08:06离开,客户3会在08:07离开,客户5会在08:10离开。

输入格式:

每个输入文件包含一个测试用例。每个测试用例第一行包含4个正整数:N(<= 20,代表窗口数),M(<= 10,代表每个窗口黄线内的最大容量),K(<= 1000,代表客户数量),以及Q(<=1000,代表查询数量)。

下一行给出K个正整数,代表K个客户的处理时间。

最后一行给出Q个正整数,代表来查询其业务处理完毕时间的客户。客户编号从1到K。

输出格式:

对来查询的Q的客户,每个用户以形式HH:MM,其中HH在[08, 17]范围内,MM在[00, 59]范围内,输出他/她的业务处理完毕时间。注意,银行每天17:00关门,对于那些17:00之前还得不到服务的客户,你需要输出“Sorry”。

输入样例:

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

输出样例:

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

知识点:队列

思路:用队列模拟每个窗口的排队过程

(1)考虑一个事实,当一位客户进入某一窗口的队列时,他的服务结束时间就已经确定了,即当前在该窗口排队的人的所有人的服务时间之和。而在所有窗口排满后,剩余客户能够去排队的时间点是所有窗口最早结束的队首客户,也就是说,在所有窗口排满的情况下,每当有一个窗口的队首客户服务结束(结束时间相同的,窗口ID小的视为先结束),剩余客户的第一个就会排到那个窗口最后面去。于是可以为窗口建立一个结构体window,存放该窗口当前队伍的最后服务时间endTime队首客户的服务结束时间popTime,并维护一个该窗口的排队队列q

(2)在8:00,只要窗口的队列没满,就把客户按照窗口编号为0 1 2 ... (N - 1) 0 1 2 ... (N - 1) 0 1 2 ...的循环顺序进行入队,且在安排的过程中不断更新窗口的endTime和popTime,其中endTime将直接作为刚入队客户的服务结束时间(即作为答案)保存下来,而popTime仅在安排每个窗口的第一个客户时更新

(3)如果(2)中已经把所有窗口排满(显然如果没有排满,就不存在剩余在黄线外的客户),那么在该步中将剩下的客户想办法入队。由(1)可知,在所有窗口排满的情况下,每当有一个窗口的队首客户服务结束(结束时间相同的,窗口ID小的视为先结束),剩余客户的第一个就会排到那个窗口最后面去。这样对每一个剩余的客户,可以选出当前所有窗口中popTime最小的窗口(popTime相同的选择窗口ID较小的),该客户将排到该窗口的队列后面,并更新该窗口的endTime和popTime,其中endTime将作为刚入队的客户的服务结束时间(即作为答案)保存下来popTime需加上队列中第一个客户出队后的新的队首客户的处理时间

(4)对每一个输入的查询客户编号,如果他的服务开始时间在17:00之后(含17:00),则输出“Sorry”;否则,输出他的服务结束时间。

时间复杂度和空间复杂度均是O(K)。

C++代码:

#include<iostream>
#include<queue>
#include<algorithm>

using namespace std;

struct window{
	int endTime, popTime;	//窗口当前队伍的最后服务时间、队首客户的服务结束时间
	queue<int> q;	//队列 
};

int changeToMinute(int hour, int minute);

int main(){
	int N, M, K, Q;
	scanf("%d %d %d %d", &N, &M, &K, &Q);
	int needTime[K];
	window windows[N];
	for(int i = 0; i < K; i++){
		scanf("%d", &needTime[i]);	//读入服务需要时间 
	}
	for(int i = 0; i < N; i++){
		windows[i].popTime = windows[i].endTime = changeToMinute(8, 0);	//初始化每个窗口的popTime和endTime为08:00 
	}
	int inIndex = 0;	//当前第一个未入队的客户编号
	int result[K];	 
	for(int i = 0; i < min(N * M, K); i++){
		windows[inIndex % N].q.push(inIndex);	//循环入队
		windows[inIndex % N].endTime += needTime[inIndex];	//更新窗口的服务结束时间endTime
		if(inIndex < N){
			windows[inIndex].popTime = needTime[inIndex];	//对窗口的第一个客户,更新popTime 
		}
		result[inIndex] = windows[inIndex % N].endTime;	//当前入队的客户的服务结束时间直接保存作为答案
		inIndex++; 
	}
	for(; inIndex < K; inIndex++){	//处理剩余客户的入队 
		int idx = -1, minPopTime = 1000000000;	//寻找所有窗口的最小popTime 
		for(int i = 0; i < N; i++){
			if(windows[i].popTime < minPopTime){
				idx = i;
				minPopTime = windows[i].popTime;
			}
		}
		//找到最小popTime的窗口编号为idex,下面更新该窗口的队列情况 
		windows[idx].q.pop();	//队首客户离开 
		windows[idx].q.push(inIndex);	//客户inIndex入队 
		windows[idx].endTime += needTime[inIndex];	//更新该窗口队列的endTime
		windows[idx].popTime += needTime[windows[idx].q.front()];	//更新该窗口的popTime
		result[inIndex] = windows[idx].endTime; 	//客户inIndex的服务结束时间为该窗口的endTime 
	}
	int num;
	for(int i = 0; i < Q; i++){
		scanf("%d", &num);	//查询客户编号
		if(result[num - 1] - needTime[num - 1] >= changeToMinute(17, 0)){
			printf("Sorry\n");
		}else{
			printf("%02d:%02d\n", result[num - 1] / 60, result[num - 1] % 60);
		} 
	}
	return 0;
}

int changeToMinute(int hour, int minute){
	return hour * 60 + minute;
}

C++解题报告:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值