虽然不难,,但是耗费了大量时间。。。
思路
- 用M个队列模拟窗口,为了简单使用了非循环数组当队列。考虑极端情况,只有一个窗口时,队列长度应该设为最大顾客数。
- 用一个队列模拟顾客。每次进黄线操作应该为从顾客队列头取一个顾客送到有空位的最短窗口队列尾
- 窗口在17:00前都接受业务,在17:00(含)之后,即使黄线内还有人也清空他,不接受。
整个流程划分为以下步骤:
(1)在某一时刻,顾客从顾客队入窗口队,直到不能入了。(条件应该为:有窗口空,有剩余顾客,时间没到下班时间)
(2)窗口处理一分钟顾客业务。有顾客处理完了,那么他就出队滚蛋,留出位置来。
(3)循环(1)(2),直到所有的窗口队都空了,结束。
顾客业务的结束时间为:开始时间+业务处理时间。
关键是在整个流程中找到顾客业务的开始点:
第一个是窗口队列空,来了第一个顾客,开始处理;第二个是前一个顾客处理完了,有下一个顾客,开始处理。
//ac
#include<stdio.h>
#include<stdlib.h>
#define maxWinSize 20
#define maxQueSize 10
#define maxCustomeSize 1001
#define dayTime 540
struct Customer{ //顾客结构体
int spend; //需要时间
int ed; //结束时刻
};
struct Queue{ //队列
int maxSize;
int size; //当前队列长度
int head; //头,指向队首元素
int rear; //尾,指向队尾元素的下一个位置
Customer* data[maxCustomeSize+2];
Queue(){
maxSize=maxQueSize;
size=0;
head=0;
rear=0;
}
};
void push(Queue* que,Customer* cus){ //压入一个顾客到队尾
if(que->size==que->maxSize){
return;
}
que->data[que->rear]=cus;
que->rear++;
que->size++;
}
Customer* pop(Queue* que){ //从队首弹出一个顾客
if(que->size==0){
return NULL;
}
que->head++;
que->size--;
return que->data[que->head-1];
}
Queue windows[maxWinSize]; //全局变量,窗口队,只用到了其中的0到N
Queue customers; //全局变量,顾客队
int shortestQue(int N,int M){ //寻找有空位的且长度最短,序号更小的窗口队
int min=100;
int minIndex=-1;
for(int i=0;i<N;i++){
if(windows[i].size==M) //如果满了,下一个
continue;
if(windows[i].size<min){ //如果长度更小,就决定是你了!(不含等可保证序号跟更小)
min=windows[i].size;
minIndex=i;
}
}
return minIndex; //如果全满,minIndex没有更新,会返回-1
}
void clear(Queue* que){ //清空队,用于超时了,但黄线内还有人
que->size=0;
que->head=que->rear;
}
void pass(int N,int *time){ //窗口处理一分钟业务
(*time)++;
for(int i=0;i<N;i++){ //对于N个窗口
if(windows[i].size!=0){ //如果窗口有人
windows[i].data[windows[i].head]->spend--; //队首人的业务剩余时间-1
if(windows[i].data[windows[i].head]->spend==0){ //如果他的业务剩余时间为0了
pop(&windows[i]); //那就踢走他
if(*time>=dayTime){ //并且看一眼我下班了没
clear(&windows[i]); //要是下班了就清空黄线内排队的人
}else if(windows[i].size!=0){ //要是没下班,并且黄线内还有人
//此时是新的顾客业务的开始点,在此时计算出他的结束时间
windows[i].data[windows[i].head]->ed=*time+windows[i].data[windows[i].head]->spend;
}
}
}
}
}
bool allWindowEmpty(int N){ //判断是否所有窗口都空了
for(int i=0;i<N;i++){
if(windows[i].size!=0){
return false;
}
}
return true;
}
int main(){
customers.size=0;
customers.maxSize=maxCustomeSize;
int N,M,K,Q;
scanf("%d %d %d %d",&N,&M,&K,&Q);
for(int i=1;i<=K;i++){
//这里使用Customer cus;会导致出错
//由于cus是局部变量,每过一遍for就会被销毁,下一次for时新申请的cus的地址和上一次的相同
//导致压入customers.data的指针全部指向同一块内存。
Customer* cus=(Customer*)malloc(sizeof(Customer));
int tmp;
scanf("%d",&tmp);
cus->spend=tmp;
cus->ed=-1;
push(&customers,cus);
}
int time=0;
while(true){
//从顾客队入窗口队
int shortest=shortestQue(N,M);
while(shortest!=-1&&customers.size!=0&&time<dayTime){
push(&windows[shortest],pop(&customers));
//这里需要判断另一个新顾客业务开始点,
//如果进窗口队后,前面没人,那此时是顾客业务的开始点,此时计算出他的业务结束时间
if(windows[shortest].size==1){
windows[shortest].data[windows[shortest].head]->ed=time+windows[shortest].data[windows[shortest].head]->spend;
}
shortest=shortestQue(N,M);
}
pass(N,&time);
if(allWindowEmpty(N)){
break;
}
}
for(int i=0;i<Q;i++){ //输出结果
int query;
scanf("%d",&query);
int ed=customers.data[query-1]->ed; //队列中的数据都在data中,直接取
if(ed==-1){ //等于-1的都是没被处理的
printf("Sorry\n");
}else{
printf("%02d:%02d\n",ed/60+8,ed%60);
}
}
}
谨此为戒,写得很繁琐,还是要努力,欢迎指点!