PAT ( Advanced Level ) 1014——模拟题又一次题面挖坑

题目传送门

一眼模拟
顾客蜂拥而至不需要考虑到达时间
无脑排队不会插队,不需要对服务时间进行排序之类的操作
问题已经非常简化过于友好了

p r o c [ K ] proc[K] proc[K]:每个人需要的业务服务时间
r e t [ K ] ret[K] ret[K]:每个人业务服务进度(剩余时间)
w a i t [ K ] wait[K] wait[K]:每个人的等待时间
a [ N ] [ K ] a[N][K] a[N][K]:N个队列的状态(从8:00开始)
c u r [ N ] cur[N] cur[N]:N个队列正在服务的顾客(1~cur[i]-1是第i个队列已经服务过的顾客)

思路很简单,开门之前先把黄线以内的位置都安排好
每次都有一个业务服务时间剩余最少的人即将离开
我们只需要从每一列的当前服务对象中,找到这个 " 最快 " 的人即可
" 最快 " 的人完成业务后,就会离开银行
这时黄线后的第一个人就可以进入原先 " 最快 " 的人所在的那个队列继续等待

" 最快 " 的人完成业务时,所有队列的当前客户的业务服务进度都需要 " 前进 ",之后就重复寻找 " 最快 " 的人,让黄线外的人进入黄线

直到所有人都进入黄线在窗口前排队

那么每个人的等待时间如何计算?
想象一下当你在任何一个地方排队的时候
等待的时间只和你选择了排哪个队有关系
每当一个 " 最快 " 的人完成业务,所有人的时间都会推移,这一点其实已经体现在每个窗口当前客户的业务服务进度
换句话说,每个人等待的时间就是
所在窗口,曾经排在ta前面的所有人,业务服务时间之和
这就可以解释我们为什么要用 a [ N ] [ K ] a[N][K] a[N][K]把在第 i i i个窗口排过队的人都记录下来

最后的输出= 排在前面的人的业务服务时间之和+自己的业务服务时间

最后一个阻碍你AC的绊脚石,就是PAT祖传的模糊描述了

刁钻测试点,都是一些题面挖的坑
测试点2和5:一个人如果开始时间在540之前(不包括540)即使结束时间超过540,依旧要服务
感谢浙大OIer提供测试数据情况

有兴趣的可以去看看其他佬的解决方案,都不错
给柳神引流了OOOooorz

Tip

数据范围看错了所以被干翻了四个点
一共就七个点,还是太着急了

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;

const int N = 1000;
const int INF = 1e9;
int K, Q, n, m;
int proc[N + 1],ret[N + 1],wait[N + 1];
int a[21][N + 1];
int ques[N + 1];
int cur[21];

int findmin() {
	int mn = INF;
	int index = -1;
	for (int i = 0; i < n; ++i) {
		if (ret[a[i][cur[i]]] < mn) {
			mn = ret[a[i][cur[i]]];   //剩余时间
			index = i;
		}
	}
	return index;
}

void mvtime(int x) {   //第x列的current顾客完成了业务,所有列的current都需要把时间推移一下
	int T = ret[a[x][cur[x]]];
	for (int i = 0; i < n; ++i) {
		ret[a[i][cur[i]]] -= T;
	}
}

void doit() {
	int index = 0;
	for (int i = 0; i < n; ++i) a[i][0] = 0;   //每一列有多少人
	for (int i = 0; i < n; ++i) cur[i] = 1;    //每一列在服务第几个人
	for (int i = 1; i <= m && index<K; ++i)    //开门时的状态
		for (int j = 0; j < n && index < K; ++j)
		{ 
			a[j][i] = ++index;   //矩阵里记录的是id
			a[j][0]++;
		}
	while (index < K) {
		int x=findmin();   //找到现在正在服务的最短时间在哪一列
		//服务完了ta就走了,x就是最短的一列,下一个人肯定就会进入x
		//接下来要把时间往后推移一下,方便之后findmin
		mvtime(x);
		a[x][0]++;
		cur[x]++;
		a[x][a[x][0]] = ++index;
	}
	//计算所有人的等待时间
	for (int i = 0; i < n; ++i) {
		int tot = 0;
		for (int j = 1; j <= a[i][0]; ++j) {
			tot += proc[a[i][j]];
			wait[a[i][j]] = tot;
		}
	}
}

void print(int T,int tt) {
	int h = T / 60;
	int min = T % 60;
	if (T-tt >= 9*60) {   //开始时间已经超过17:00
		cout << "Sorry";
		return;
	}
	h += 8;
	if (h <= 9)
		cout << "0" << h << ":";
	else cout << h << ":";
	if (min <= 9) 
		cout << "0" << min;
	else cout << min;
}

int main()
{
	cin >> n >> m >> K >> Q;
	for (int i = 1; i <= K; ++i) { cin >> proc[i]; ret[i] = proc[i]; }
	for (int i = 1; i <= Q; ++i) cin >> ques[i];
	doit();
	for (int i = 1; i <= Q; ++i) {
		print(wait[ques[i]],proc[ques[i]]);
		if (i < Q) cout << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值