1026 Table Tennis (30分)
A table tennis club has N tables available to the public. The tables are numbered from 1 to N. For any pair of players, if there are some tables open when they arrive, they will be assigned to the available table with the smallest number. If all the tables are occupied, they will have to wait in a queue. It is assumed that every pair of players can play for at most 2 hours.
Your job is to count for everyone in queue their waiting time, and for each table the number of players it has served for the day.
One thing that makes this procedure a bit complicated is that the club reserves some tables for their VIP members. When a VIP table is open, the first VIP pair in the queue will have the privilege to take it. However, if there is no VIP in the queue, the next pair of players can take it. On the other hand, if when it is the turn of a VIP pair, yet no VIP table is available, they can be assigned as any ordinary players.
Input Specification:
Each input file contains one test case. For each case, the first line contains an integer N (≤10000) - the total number of pairs of players. Then N lines follow, each contains 2 times and a VIP tag: HH:MM:SS - the arriving time, P - the playing time in minutes of a pair of players, and tag - which is 1 if they hold a VIP card, or 0 if not. It is guaranteed that the arriving time is between 08:00:00 and 21:00:00 while the club is open. It is assumed that no two customers arrives at the same time. Following the players’ info, there are 2 positive integers: K (≤100) - the number of tables, and M (< K) - the number of VIP tables. The last line contains M table numbers.
Output Specification:
For each test case, first print the arriving time, serving time and the waiting time for each pair of players in the format shown by the sample. Then print in a line the number of players served by each table. Notice that the output must be listed in chronological order of the serving time. The waiting time must be rounded up to an integer minute(s). If one cannot get a table before the closing time, their information must NOT be printed.
Sample Input:
9
20:52:00 10 0
08:00:00 20 0
08:02:00 30 0
20:51:00 10 0
08:10:00 5 0
08:12:00 10 1
20:50:00 10 0
08:01:30 15 1
20:53:00 10 1
3 1
2
Sample Output:
08:00:00 08:00:00 0
08:01:30 08:01:30 0
08:02:00 08:02:00 0
08:12:00 08:16:30 5
08:10:00 08:20:00 10
20:50:00 20:50:00 0
20:51:00 20:51:00 0
20:52:00 20:52:00 0
3 3 2
这真的是我目前为止做过最坑的一道题,无数个坑,我的心态和逻辑无数次崩塌。
本来以为和银行排队那两个题差不多,我还窃喜呢,结果,没想到加了个VIP能搞到那么复杂。
总体思路就是:
- 输入的时候如果顾客的用餐时间在两小时以上,压缩到两小时。
- 将所有顾客按照先来后到的顺序排序。
- 如果第一个顾客到达时间在打烊之前,给他安排一个桌子坐下。
3.1 如果他是VIP顾客,且有VIP桌子,给他安排号数最小的VIP桌子;
3.2 如果他是VIP顾客但没有VIP桌子,或他不是VIP顾客,给他安排号数最小的桌子。 - 如果第一个顾客到达时间就在打烊之后,直接结束。
- 寻找最先空出来的桌子。
5.1 如果这张桌子本来就是空的,它空出来的时间就是桌子当前时间。
5.2 如果这张桌子当前有人,它空出来的时间就是当前顾客吃完的时间。 - 如果找到的这张最先空出来的桌子本身是有人的,
6.1 如果当前的这个顾客开始接受服务的时间在打烊之前,将该顾客加入结果数组并记录其开始接受服务的时间。
6.2 更新桌子时间并把桌子清空。 - 如果后面还有人,给他安排位置,并跳到5继续循环,直到所有桌子全空了。
7.1 如果即将接受服务的这个人的到达时间在打烊之后,则把当前还在吃的桌子全部清空,并按照6.1的思路更新相关数据后结束。
7.2 如果即将接受服务的这个人已经接受过服务了,那就判断下一个。
7.3 如果即将接受服务的这个人到达时间比找到的这个桌子的时间晚,
7.3.1 如果这个桌子是VIP桌子,不管这个人是不是VIP,都直接上桌。
(因为他来的时候这个桌子已经空了,就算他不是VIP且后面有VIP,VIP来的时间也肯定比他晚,
也就是说他来的时候VIP还没来,不可能说放着空桌子不让坐)
7.3.2 如果这个桌子不是VIP桌子,这个人也不是VIP,那就直接坐。
7.3.3 如果这个桌子不是VIP桌子,但这个人是VIP,找他来的时候号数最小的空的VIP桌子,没有的话就还是坐这个桌子。
7.4 如果即将接受服务的这个人到达时间比找到的这个桌子的时间早,
7.4.1 如果这个桌子是VIP桌子,这个人也是VIP的话,直接坐。
7.4.2 如果这个桌子是VIP桌子,但这个人不是VIP的话,找后面第一个VIP,如果VIP到达时间比这个桌子时间早或等,就让VIP坐。否则还是给这个人。
7.4.3 如果这个桌子不是VIP桌子,不管这个人是不是VIP都直接坐。 - 如果后面没人了,把当前还在吃的桌子全部清空,并按照6.1的思路更新相关数据。
- 输出相关数据。
几个注意点:
- 用餐超过2小时要压缩为2小时。
- 21点及其之后到的人不能接受服务。
- VIP用户到的时候如果有VIP桌子空着,用编号最小的VIP桌子。
- 等待的时间要四舍五入。
卡住的几个测试点:
- 测试点3:可能出现运行时错误或运行超时。
这个测试点就是测的到达时间在21点及其之后是不能接受服务的这点。
样例输入:
2
20:00:00 10 0
21:00:00 10 0
3 1
2
样例输出:
20:00:00 20:00:00 0
1 0 0
- 测试点5:
VIP来的时候,如果有多个VIP桌子空出来,选择VIP桌号最小的而不是普通桌号最小的。
样例输入:
4
06:00:00 30 1
08:00:00 30 1
10:00:00 30 1
12:00:00 30 1
5 1
3
样例输出:
06:00:00 08:00:00 120
08:00:00 08:00:00 0
10:00:00 10:00:00 0
12:00:00 12:00:00 0
1 0 3 0 0
- 测试点7和8:
样例输入:
3
07:59:31 60 1
07:59:29 60 1
08:00:30 100 1
2 1
1
样例输出:
1
07:59:29 08:00:00 1
07:59:31 08:00:00 0
08:00:30 09:00:00 60
2 1
思路写的相当繁琐,代码也一样,所以,即使是我自己也不想再看一遍,
https://www.cnblogs.com/wsshub/p/12556493.html
这篇博客的思路写的好些,虽然我没看他的思路,但是我看了他的测试用例,从而找到了自己的bug
AC代码
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
// 顾客数据结构
struct customer{
int arr; // 到达的时间
int play; // 用餐时间
int start; // 开始接受服务的时间
int fvip; // 是否是VIP的标记
int serve=0;
};
int n; // 顾客总数
vector<customer> customers; // 所有顾客
int tableNum,VIPTableNum; // 分别表示桌子总数和VIP桌子总数
vector<int> VIPTable; // 表示VIP桌子的号数
queue<int> tableQ[101]; // 每张桌子服务的顾客的id
vector<int> tableTime; // 桌子的时间线
vector<int> servenum; // 桌子的服务人数
vector<customer> ans; // 满足条件最后要输出的顾客
// 判断是不是所有桌子都空了
bool allEmpty(){
for(int i=0;i<tableNum;i++){
if(!tableQ[i].empty()){
return false;
}
}
return true;
}
// 按照时间先后顺序排序
bool cmp(customer a,customer b){
return a.arr<b.arr;
}
// 按照服务时间先后顺序排序
bool cmp1(customer a,customer b){
return a.start<b.start;
}
int main(){
scanf("%d",&n);
customers.resize(n);
for(int i=0;i<n;i++){
int h,m,s,p;
scanf("%d:%d:%d %d %d",&h,&m,&s,&p,&customers[i].fvip);
customers[i].arr=h*3600+m*60+s;
customers[i].play=p*60;
// 用餐时间在两小时以上的要压缩到两小时
customers[i].play = min(7200, customers[i].play);
}
scanf("%d %d",&tableNum,&VIPTableNum);
VIPTable.resize(VIPTableNum);
for(int i=0;i<VIPTableNum;i++){
scanf("%d ",&VIPTable[i]);
}
// 将顾客按照先来后到的顺序排序
sort(customers.begin(),customers.end(),cmp);
// 初始化数组
tableTime.resize(tableNum);
fill(tableTime.begin(),tableTime.end(),8*3600);
servenum.resize(tableNum);
fill(servenum.begin(),servenum.end(),0);
// 一开始只需要让第一个来的顾客上桌
int now=0;
// 当前前提是这个顾客来的时间在打烊之前
if(now<n && customers[now].arr<21*3600){
// 如果这个顾客是VIP,就找号数最小的VIP桌子给他
int f=0; // 标记是否找到VIP桌子坐下了
if(customers[now].fvip==1){
for(int i=0;i<VIPTableNum;i++){
int table=VIPTable[i]-1;
// 找到了就让他坐下
if(tableQ[table].empty()){
tableQ[table].push(now);
customers[now].serve=1;
now++;
f=1;
break; // break不可少!
}
}
}
// 如果这个顾客是VIP但是没有VIP桌子,或者这个顾客不是VIP,就直接找号数最小的桌子坐下
else if(f==0 || customers[now].fvip==0){
for(int i=0;i<tableNum;i++){
if(tableQ[i].empty()){
tableQ[i].push(now);
customers[now].serve=1;
now++;
break; // break不可少!
}
}
}
}
// 如果来的第一个顾客就在打烊之后了,那可以直接结束了,后面就算还有顾客也没必要判断了
else{
for(int i=0;i<tableNum;i++){
printf("%d",servenum[i]);
if(i!=tableNum-1) printf(" ");
else printf("\n");
}
return 0;
}
// 只要不是所有桌子全空了,就继续循环
while(!allEmpty()){
// 找最早空出来的桌子
int minleft=25*3600; // 当前正在吃的最早吃完的
int mintindex; // 最早吃完的桌子的index
for(int i=0;i<tableNum;i++){
// 如果这个桌子本来就是空的,那它最早空出来的时间就是现在桌子的时间;
// 如果这个桌子上现在有人,那它最早空出来的时间就是这个顾客吃完的时间。
int left=tableTime[i];
if(!tableQ[i].empty()){
int cindex=tableQ[i].front();
left=max(tableTime[i],customers[cindex].arr)+customers[cindex].play;
}
if(left<minleft){
minleft=left;
mintindex=i;
}
}
// 如果最早空出来的这张桌子原本是有人的,就让这个人离开
if(!tableQ[mintindex].empty()){
int c=tableQ[mintindex].front();
// 如果这个顾客开始接受服务的时间在打烊之前,就把这个顾客加入结果数组,并更新相关数据
if(max(tableTime[mintindex],customers[c].arr)<21*3600){
servenum[mintindex]++;
customers[c].start=max(tableTime[mintindex],customers[c].arr);
ans.push_back(customers[c]);
}
tableTime[mintindex]=max(tableTime[mintindex],customers[c].arr) + customers[c].play;
tableQ[mintindex].pop();
}
// 如果后面已经没有等待服务的顾客了,那就把当前还在吃的桌子全部清空,并更新相关数据
if(now==n){
for(int i=0;i<tableNum;i++){
if(!tableQ[i].empty()){
int c=tableQ[i].front();
if(max(tableTime[i],customers[c].arr)<21*3600){
servenum[i]++;
customers[c].start=max(tableTime[i],customers[c].arr);
ans.push_back(customers[c]);
}
tableQ[i].pop();
}
}
}
// 如果当前顾客已经接受过服务了,那就继续判断下一个顾客
if(now<n && customers[now].serve==1) now++;
// 如果当前顾客来的时间在打烊之前,就可以安排他到空桌子上
if(now<n && customers[now].arr<21*3600){
// now到的时间比桌子空出来的时间晚,说明他来的时候很可能不止这一张桌子空出来了,
if(customers[now].arr >= tableTime[mintindex]){
// 当前桌子是VIP桌子
if(find(VIPTable.begin(),VIPTable.end(),mintindex+1)!=VIPTable.end()){
// now是不是VIP都直接提供服务。
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
// 当前桌子不是VIP桌子
else{
// now是VIP
if(customers[now].fvip==1){
// 遍历所有VIP桌子,优先选择号数小的空的VIP桌子,没有就还是坐这张。
int f=0;
for(int i=0;i<VIPTableNum;i++){
int table=VIPTable[i]-1;
if(tableQ[table].empty() || (max(tableTime[table],customers[tableQ[table].front()].arr)+customers[tableQ[table].front()].play) <= customers[now].arr){
f=1;
if(max(tableTime[table],customers[tableQ[table].front()].arr)<21*3600){
servenum[table]++;
customers[tableQ[table].front()].start=max(tableTime[table],customers[tableQ[table].front()].arr);
ans.push_back(customers[tableQ[table].front()]);
}
tableTime[table]=max(tableTime[table],customers[tableQ[table].front()].arr) + customers[tableQ[table].front()].play;
tableQ[table].pop();
tableQ[table].push(now);
customers[now].serve=1;
now++;
break;
}
}
// 没有空的VIP桌子就还是做这张
if(f==0){
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
}
// now不是VIP
else{
// 直接提供服务。
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
}
}
// now到的时间比桌子的时间早,说明他来的时候桌子都是满的,
else{
// 当前桌子是VIP桌子
if(find(VIPTable.begin(),VIPTable.end(),mintindex+1)!=VIPTable.end()){
// now是VIP
if(customers[now].fvip==1){
// 直接提供服务
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
// now不是VIP
else{
// 找后面第一个VIP
int vip=now;
while(vip<n){
if(customers[vip].fvip==1 && customers[vip].serve==0){
break;
}
vip++;
}
// 如果有VIP且他到的时间比桌子的时间早或等就给他;
if(vip<n && customers[vip].arr<=tableTime[mintindex]){
tableQ[mintindex].push(vip);
customers[vip].serve=1;
}
// 反之还是给now。
else{
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
}
}
// 当前桌子不是VIP桌子
else{
// now是不是VIP都直接提供服务
tableQ[mintindex].push(now);
customers[now].serve=1;
now++;
}
}
}
// 如果当前顾客来的时间在打烊之后,就让now=n去清空当前还在吃的桌子,并更新相关数据
else{
now=n;
}
}
// 按照服务时间先后顺序排序
sort(ans.begin(),ans.end(),cmp1);
// 输出顾客来的时间,开始接受服务的时间,等待时间(分钟,四舍五入)
for(int i=0;i<ans.size();i++){
int h1 = ans[i].arr/3600;
int m1 = ans[i].arr%3600/60;
int s1 = ans[i].arr%60;
int h2 = ans[i].start/3600;
int m2 = ans[i].start%3600/60;
int s2 = ans[i].start%60;
int d=(ans[i].start-ans[i].arr)/60;
if((ans[i].start-ans[i].arr)%60 >= 30){
d+=1;
}
printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",h1,m1,s1,h2,m2,s2,d);
}
// 输出每张桌子服务过的人数
for(int i=0;i<tableNum;i++){
printf("%d",servenum[i]);
if(i!=tableNum-1) printf(" ");
else printf("\n");
}
return 0;
}