题目链接
题目不难读懂,并且不涉及什么算法,但是逻辑上非常繁琐,算法笔记上有非常详细的题意解释,这里就不赘述了。
这里主要是写我用的一种模拟时间滴答的方法,逻辑上可能要简单一些。
主要就是将题目中的时间 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 46800
int 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; //是否是VIP
bool servered = false; //是否已被服务
};
int n, tableNum, viptableNum, minemptytable; //记录数、桌子总数,vip桌子总数,索引号最小的空桌子的索引
int timeStamp; //时间戳,8:00:00为0,21:00:00为46800
int 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,则该桌子为空,考虑更新minemptytable
int i;
for (i = 0; i < tableNum; i++) {
if (tables[i] > 0)
tables[i]--;
if (tables[i] == 0)
if (i < minemptytable) //考虑更新minemptytable
minemptytable = i;
}
}
void serverPeople(int peopleIndex, int tableIndex)
{
//服务playpairs中索引号为peopleIndex的玩家,使用的桌子为tableIndex
playpairs[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;
}
/*
//input
2
08:00:00 10 1
08:05:00 10 1
3 2
2 3
//output
08:00:00 08:00:00 0
08:05:00 08:05:00 0
0 1 1
*/
/*
//input
2
20:00:00 60 0
20:30:00 10 1
1 1
1
//output
20:00:00 20:00:00 0
1
*/
最下面是算法笔记上给出的几组测试数据。
这种方法虽然外层循环有46800次,但是内层大部分时间只是一个简单的判断,没有实际操作,所以耗时并不高。