题目链接
    题目不难读懂,并且不涉及什么算法,但是逻辑上非常繁琐,算法笔记上有非常详细的题意解释,这里就不赘述了。

    这里主要是写我用的一种模拟时间滴答的方法,逻辑上可能要简单一些。
    主要就是将题目中的时间 08:00:00 - 21:00:00 以每秒一个间隔,总共 13*60*60=46800 个时钟滴答,在每一个始终滴答到来的时候,依次处理玩家到达事件、桌子使用计时减1事件、空桌子安排玩家使用事件。
    这样思考总体上逻辑简洁一些。

    具体的看代码

    1. #include<bits/stdc++.h>
    2. using namespace std;
    3. #define MAXPEOPLE 10005
    4. #define MAXTABLE 105
    5. #define TIME2SECOND(h,m,s) ((((h) - 8) * 3600) + ((m) * 60) + (s))
    6. #define MINUTE2SECOND(m) ((m) * 60)
    7. #define MAXTIMESTAMP 46800
    8. int second2minute(int s)
    9. {
    10. //秒数转分钟,并且舍入
    11. int m = s / 60;
    12. if ((s % 60) >= 30)
    13. m++;
    14. return m;
    15. }
    16. void printTime(int second)
    17. {
    18. //根据秒数打印时间
    19. int h, m, s;
    20. h = second / 3600 + 8;
    21. second = second % 3600;
    22. m = second / 60;
    23. second = second % 60;
    24. s = second;
    25. printf("%02d:%02d:%02d ", h, m, s);
    26. }
    27. struct People
    28. {
    29. int arrTime = 0; //到达时间
    30. int needTime = 0; //需要的时间
    31. int serverTime = -1; //服务开始时间
    32. bool VIP = false; //是否是VIP
    33. bool servered = false; //是否已被服务
    34. };
    35. int n, tableNum, viptableNum, minemptytable; //记录数、桌子总数,vip桌子总数,索引号最小的空桌子的索引
    36. int timeStamp; //时间戳,8:00:00为0,21:00:00为46800
    37. int serverIndex, arriveIndex; //当前未服务玩家的索引、当前可服务玩家索引 [serverIndex,arriveIndex)为当前时间
    38. //可服务但尚未服务的玩家
    39. queue<int> VIPwaitIndex; //在等待中的vip玩家
    40. People playpairs[MAXPEOPLE]; //玩家记录
    41. int tables[MAXTABLE] = { 0 }; //当前桌子还要用多久,0代表空桌子
    42. bool isVIP[MAXTABLE] = { false }; //记录桌子是不是VIP桌
    43. int serverNum[MAXTABLE] = { 0 }; //每个桌子服务过的人数
    44. vector<int> VIPtable; //记录vip桌子的索引
    45. void dealServerIndex();
    46. void handelTableUse()
    47. {
    48. //在每一个时钟滴答到来时,将正在使用的桌子的使用时间减少1,如果减少后为0,则该桌子为空,考虑更新minemptytable
    49. int i;
    50. for (i = 0; i < tableNum; i++) {
    51. if (tables[i] > 0)
    52. tables[i]--;
    53. if (tables[i] == 0)
    54. if (i < minemptytable) //考虑更新minemptytable
    55. minemptytable = i;
    56. }
    57. }
    58. void serverPeople(int peopleIndex, int tableIndex)
    59. {
    60. //服务playpairs中索引号为peopleIndex的玩家,使用的桌子为tableIndex
    61. playpairs[peopleIndex].servered = true; //标记已服务
    62. playpairs[peopleIndex].serverTime = timeStamp; //记录服务时间
    63. tables[tableIndex] = playpairs[peopleIndex].needTime; //开始桌子时间的倒计时
    64. serverNum[tableIndex]++; //记录该桌子的服务人数
    65. while (minemptytable < tableNum && tables[minemptytable]>0) {
    66. //更新minemptytable,这里一定要用while,因为空桌子不是连续的
    67. minemptytable++;
    68. }
    69. dealServerIndex();
    70. }
    71. void dealServerIndex()
    72. {
    73. //更新待服务玩家索引serverIndex,这里一定要用while,因为可能存在有的玩家因为VIP插队的原因已经被服务了
    74. while (playpairs[serverIndex].servered) {
    75. serverIndex++;
    76. }
    77. }
    78. void dealVIPtable()
    79. {
    80. //处理空的VIP专属桌子
    81. for (int t : VIPtable) {
    82. if (tables[t] == 0 && !VIPwaitIndex.empty()) {
    83. //桌子为空且有VIP玩家在等待,则服务这个玩家
    84. int p = VIPwaitIndex.front();
    85. VIPwaitIndex.pop();
    86. serverPeople(p, t);
    87. }
    88. }
    89. }
    90. void handelEmptyTable()
    91. {
    92. //在每一个时钟滴答,把桌子的使用倒计时-1后,处理所有的空桌子
    93. dealVIPtable(); //首先处理VIP桌子,因为如果等待队列中的第一人是VIP,
    94. //且同时存在VIP桌子和普通桌子时,优先选择VIP桌子
    95. //按序处理所有空桌子
    96. while (minemptytable < tableNum && serverIndex < arriveIndex) {
    97. if (playpairs[serverIndex].VIP) {
    98. //如果排在第一的是一个VIP玩家,则他一定没有VIP桌子可用了,所以他用了一个普通桌子,
    99. //因此不会在dealVIPtable()中出对,故要移出VIP等待队列
    100. VIPwaitIndex.pop();
    101. }
    102. serverPeople(serverIndex, minemptytable);
    103. }
    104. }
    105. void handelPeopleArrive()
    106. {
    107. //在每一个时钟滴答,如果到了下一个玩家到来的时间,就将arriveIndex++,代表到达了
    108. //一个新的玩家,并考虑他是不是VIP,是则加入VIP等待队列
    109. if (playpairs[arriveIndex].arrTime == timeStamp) {
    110. if (playpairs[arriveIndex].VIP) {
    111. VIPwaitIndex.push(arriveIndex);
    112. }
    113. arriveIndex++;
    114. }
    115. }
    116. int main() {
    117. int h, m, s, i, need, tag;
    118. scanf("%d", &n);
    119. for (i = 0; i < n; i++) {
    120. //玩家信息输入
    121. scanf("%d:%d:%d %d %d", &h, &m, &s, &need, &tag);
    122. if (need > 120) {
    123. need = 120;
    124. }
    125. playpairs[i].arrTime = TIME2SECOND(h, m, s);
    126. playpairs[i].needTime = MINUTE2SECOND(need);
    127. if (tag == 1)
    128. playpairs[i].VIP = true;
    129. }
    130. scanf("%d %d", &tableNum,&viptableNum);
    131. for (i = 0; i < viptableNum; i++) {
    132. //桌子信息输入
    133. int v;
    134. scanf("%d", &v);
    135. VIPtable.push_back(--v); //将桌子索引转为0开始
    136. isVIP[v] = true;
    137. }
    138. //按照玩家到达时间升序排列
    139. sort(playpairs, playpairs + n, [](People& a, People& b) {return a.arrTime < b.arrTime; });
    140. serverIndex = 0;
    141. minemptytable = 0;
    142. arriveIndex = 0;
    143. for (timeStamp = 0; timeStamp < MAXTIMESTAMP; timeStamp++) {
    144. //每一个时间戳,首先处理玩家到达事件,再处理桌子使用计时减1事件,最后处理
    145. //空桌子使用事件,三者的顺序不能颠倒
    146. handelPeopleArrive();
    147. handelTableUse();
    148. handelEmptyTable();
    149. if (serverIndex == arriveIndex == n)
    150. //所有玩家都已经服务了
    151. break;
    152. }
    153. for (i = serverIndex; i < arriveIndex; i++) {
    154. //在[serverIndex,arriveIndex)中,可能存在因为VIP插队而已被服务的玩家,故要
    155. //把该玩家提前,这样最后在[0,serverIndex)中的就是全部服务过的玩家
    156. if (playpairs[i].servered) {
    157. playpairs[serverIndex++] = playpairs[i];
    158. }
    159. }
    160. //根据服务时间升序排列
    161. sort(playpairs, playpairs + serverIndex, [](People& a, People& b) {return a.serverTime < b.serverTime; });
    162. //答案输出
    163. for (i = 0; i < serverIndex; i++) {
    164. printTime(playpairs[i].arrTime);
    165. printTime(playpairs[i].serverTime);
    166. printf("%d\n", second2minute(playpairs[i].serverTime - playpairs[i].arrTime));
    167. }
    168. printf("%d", serverNum[0]);
    169. for (i = 1; i < tableNum; i++) {
    170. printf(" %d", serverNum[i]);
    171. }
    172. return 0;
    173. }
    174. /*
    175. //input
    176. 2
    177. 08:00:00 10 1
    178. 08:05:00 10 1
    179. 3 2
    180. 2 3
    181. //output
    182. 08:00:00 08:00:00 0
    183. 08:05:00 08:05:00 0
    184. 0 1 1
    185. */
    186. /*
    187. //input
    188. 2
    189. 20:00:00 60 0
    190. 20:30:00 10 1
    191. 1 1
    192. 1
    193. //output
    194. 20:00:00 20:00:00 0
    195. 1
    196. */

    最下面是算法笔记上给出的几组测试数据。
    PAT1026 Table Tennis (30分)时间戳法 - 图1
    这种方法虽然外层循环有46800次,但是内层大部分时间只是一个简单的判断,没有实际操作,所以耗时并不高。