题目链接
题目不难读懂,并且不涉及什么算法,但是逻辑上非常繁琐,算法笔记上有非常详细的题意解释,这里就不赘述了。
这里主要是写我用的一种模拟时间滴答的方法,逻辑上可能要简单一些。
主要就是将题目中的时间 08:00:00 - 21:00:00 以每秒一个间隔,总共 13*60*60=46800 个时钟滴答,在每一个始终滴答到来的时候,依次处理玩家到达事件、桌子使用计时减1事件、空桌子安排玩家使用事件。
这样思考总体上逻辑简洁一些。
具体的看代码 :
#include<bits/stdc++.h>using namespace std;#define MAXPEOPLE 10005#define MAXTABLE 105#define TIME2SECOND(h,m,s) ((((h) - 8) * 3600) + ((m) * 60) + (s))#define MINUTE2SECOND(m) ((m) * 60)#define MAXTIMESTAMP 46800int second2minute(int s){//秒数转分钟,并且舍入int m = s / 60;if ((s % 60) >= 30)m++;return m;}void printTime(int second){//根据秒数打印时间int h, m, s;h = second / 3600 + 8;second = second % 3600;m = second / 60;second = second % 60;s = second;printf("%02d:%02d:%02d ", h, m, s);}struct People{int arrTime = 0; //到达时间int needTime = 0; //需要的时间int serverTime = -1; //服务开始时间bool VIP = false; //是否是VIPbool servered = false; //是否已被服务};int n, tableNum, viptableNum, minemptytable; //记录数、桌子总数,vip桌子总数,索引号最小的空桌子的索引int timeStamp; //时间戳,8:00:00为0,21:00:00为46800int serverIndex, arriveIndex; //当前未服务玩家的索引、当前可服务玩家索引 [serverIndex,arriveIndex)为当前时间//可服务但尚未服务的玩家queue<int> VIPwaitIndex; //在等待中的vip玩家People playpairs[MAXPEOPLE]; //玩家记录int tables[MAXTABLE] = { 0 }; //当前桌子还要用多久,0代表空桌子bool isVIP[MAXTABLE] = { false }; //记录桌子是不是VIP桌int serverNum[MAXTABLE] = { 0 }; //每个桌子服务过的人数vector<int> VIPtable; //记录vip桌子的索引void dealServerIndex();void handelTableUse(){//在每一个时钟滴答到来时,将正在使用的桌子的使用时间减少1,如果减少后为0,则该桌子为空,考虑更新minemptytableint i;for (i = 0; i < tableNum; i++) {if (tables[i] > 0)tables[i]--;if (tables[i] == 0)if (i < minemptytable) //考虑更新minemptytableminemptytable = i;}}void serverPeople(int peopleIndex, int tableIndex){//服务playpairs中索引号为peopleIndex的玩家,使用的桌子为tableIndexplaypairs[peopleIndex].servered = true; //标记已服务playpairs[peopleIndex].serverTime = timeStamp; //记录服务时间tables[tableIndex] = playpairs[peopleIndex].needTime; //开始桌子时间的倒计时serverNum[tableIndex]++; //记录该桌子的服务人数while (minemptytable < tableNum && tables[minemptytable]>0) {//更新minemptytable,这里一定要用while,因为空桌子不是连续的minemptytable++;}dealServerIndex();}void dealServerIndex(){//更新待服务玩家索引serverIndex,这里一定要用while,因为可能存在有的玩家因为VIP插队的原因已经被服务了while (playpairs[serverIndex].servered) {serverIndex++;}}void dealVIPtable(){//处理空的VIP专属桌子for (int t : VIPtable) {if (tables[t] == 0 && !VIPwaitIndex.empty()) {//桌子为空且有VIP玩家在等待,则服务这个玩家int p = VIPwaitIndex.front();VIPwaitIndex.pop();serverPeople(p, t);}}}void handelEmptyTable(){//在每一个时钟滴答,把桌子的使用倒计时-1后,处理所有的空桌子dealVIPtable(); //首先处理VIP桌子,因为如果等待队列中的第一人是VIP,//且同时存在VIP桌子和普通桌子时,优先选择VIP桌子//按序处理所有空桌子while (minemptytable < tableNum && serverIndex < arriveIndex) {if (playpairs[serverIndex].VIP) {//如果排在第一的是一个VIP玩家,则他一定没有VIP桌子可用了,所以他用了一个普通桌子,//因此不会在dealVIPtable()中出对,故要移出VIP等待队列VIPwaitIndex.pop();}serverPeople(serverIndex, minemptytable);}}void handelPeopleArrive(){//在每一个时钟滴答,如果到了下一个玩家到来的时间,就将arriveIndex++,代表到达了//一个新的玩家,并考虑他是不是VIP,是则加入VIP等待队列if (playpairs[arriveIndex].arrTime == timeStamp) {if (playpairs[arriveIndex].VIP) {VIPwaitIndex.push(arriveIndex);}arriveIndex++;}}int main() {int h, m, s, i, need, tag;scanf("%d", &n);for (i = 0; i < n; i++) {//玩家信息输入scanf("%d:%d:%d %d %d", &h, &m, &s, &need, &tag);if (need > 120) {need = 120;}playpairs[i].arrTime = TIME2SECOND(h, m, s);playpairs[i].needTime = MINUTE2SECOND(need);if (tag == 1)playpairs[i].VIP = true;}scanf("%d %d", &tableNum,&viptableNum);for (i = 0; i < viptableNum; i++) {//桌子信息输入int v;scanf("%d", &v);VIPtable.push_back(--v); //将桌子索引转为0开始isVIP[v] = true;}//按照玩家到达时间升序排列sort(playpairs, playpairs + n, [](People& a, People& b) {return a.arrTime < b.arrTime; });serverIndex = 0;minemptytable = 0;arriveIndex = 0;for (timeStamp = 0; timeStamp < MAXTIMESTAMP; timeStamp++) {//每一个时间戳,首先处理玩家到达事件,再处理桌子使用计时减1事件,最后处理//空桌子使用事件,三者的顺序不能颠倒handelPeopleArrive();handelTableUse();handelEmptyTable();if (serverIndex == arriveIndex == n)//所有玩家都已经服务了break;}for (i = serverIndex; i < arriveIndex; i++) {//在[serverIndex,arriveIndex)中,可能存在因为VIP插队而已被服务的玩家,故要//把该玩家提前,这样最后在[0,serverIndex)中的就是全部服务过的玩家if (playpairs[i].servered) {playpairs[serverIndex++] = playpairs[i];}}//根据服务时间升序排列sort(playpairs, playpairs + serverIndex, [](People& a, People& b) {return a.serverTime < b.serverTime; });//答案输出for (i = 0; i < serverIndex; i++) {printTime(playpairs[i].arrTime);printTime(playpairs[i].serverTime);printf("%d\n", second2minute(playpairs[i].serverTime - playpairs[i].arrTime));}printf("%d", serverNum[0]);for (i = 1; i < tableNum; i++) {printf(" %d", serverNum[i]);}return 0;}/*//input208:00:00 10 108:05:00 10 13 22 3//output08:00:00 08:00:00 008:05:00 08:05:00 00 1 1*//*//input220:00:00 60 020:30:00 10 11 11//output20:00:00 20:00:00 01*/
最下面是算法笔记上给出的几组测试数据。
这种方法虽然外层循环有46800次,但是内层大部分时间只是一个简单的判断,没有实际操作,所以耗时并不高。
