如果这个桌子是为vip预留的:
3.1 如果这个人是vip,这个桌子分配给他。i++ 处理下一个人
3.2 这个人不是vip,除非他后面没有vip到来,桌子才给他用:
找到他后面第一个vip;
如果vip不存在,那么这个桌子给直接给他用。
如果vip存在,那么这个桌子给vip用,对吗???注意这是错误的!!!除非这个vip在这个桌子当前服务结束之前来了,这个vip才能插队啊。他都没来插什么队。
你不是说vip存在了吗?存在了为什么又说他没来。你要区分现实生活和程序的区别,现实生活中你一眼看到你后面没有人你说不存在,但程序一次性把所有人信息都保存在数组里了,你肉眼去看数组元素肯定存在啊,但是你要去看他的到达时间是不是在这个窗口结束当前服务之间,如果不是,那就是说这个窗口结束服务了,但是,我后面那个所谓”存在“的vip没有到达,不就相当于没有吗?好好想一下,我就是漏了这个一个条件,只得了15分,就在if里面加上一个 && xxxx,立马满分!
如果这个桌子是普通桌子:
4.1 如果这个人是普通人,那么这个桌子分配给他。i++ 处理下一个人
4.2 如果这个人是vip,首先去看在他来之前有没有空下来的vip桌子,如果有,就让他去那个vip桌子,如果没有,就把这个普通桌子给他用。i++ 处理下一个人。
现在我们要处理两个问题
分配桌子是干嘛?
把某个桌子分配给某个玩家就是:更新这个玩家开始被服务的时间;更新这个桌子开始为这个玩家服务后,结束服务的时间
void AssignTableForPlayer(int t_id, int p_id) { // 玩家来的时候,这个桌子已空闲,玩家可以直接开始玩 if (table[t_id].end_time <= player[p_id].arrive_time) { player[p_id].start_time = player[p_id].arrive_time; } else { // 玩家来的时候这个桌子还在服务上一个人,需要等它当前服务结束 // 所以玩家开始玩的时间应该是这个桌子当前服务结束的时间 player[p_id].start_time = table[t_id].end_time; } // 开始新的服务,更新这个桌子当前服务的结束时间 table[t_id].end_time = player[p_id].start_time + player[p_id].play_time; // 这个桌子的服务人数增加 table[t_id].serve_count++; }
找到这个玩家后面第一个vip的详细过程是什么?
多说无益,直接看代码
int FindNextVipPlayer(int p_id) { p_id++; while (p_id < player.size()) { if (player[p_id].isvip) return p_id; p_id++; } // 不存在就返回-1 return -1; }
不是说两个问题吗?哪来的3呢?是两个问题,但是第2个问题有一个大坑啊
假如说这个队伍是这样的:1 2 3 4 v1 5 v2
那我问你,处理 1号时找到他后面第一个vip是v1,v1优先被服务了,处理2号时,他后面第一个vip还是v1,难道再给v1服务一次??不可能嘛,
现实生活中,一个人被服务后就离开了,但我们这是个数组啊,除非你把那个位置数据删了,否则他就在那,但是你如果删了那不是要数组元素顺序后移或者前移?这浪费的时间可就多了,而且很可能因为粗心大意导致数组越界访问。
所以我另外创建了一个bool served[10000],初始化为false,当某个vip被服务后就把对应位置置为true,避免被重复选取。所以我 FindNextVipPlayer 也就变成了这样:
int FindNextVipPlayer(int p_id) { p_id++; while (p_id < player.size()) { // 是会员!且未被服务! if (player[p_id].isvip && !served[p_id]) return p_id; p_id++; } return -1; }同时,在上面 四种分类讨论 中,对于碰到的vip玩家,也要加上是不是已经服务过的判断,如果是就直接跳过它服务下一个人。这一点我在代码注释里写的很详细,大家自己看吧。
满分代码这注释你要是还看不明白,尽管来打死我!!!!
#include <algorithm> #include <cmath> #include <climits> #include <iostream> #include <vector> using namespace std; // 俱乐部开门时间,以 s 为单位 int club_open_time = 3600 * 8; // 俱乐部关门时间,以 s 为单位 int club_close_time = 3600 * 21; // 玩家 struct Player { // 到达时间,开始时间,玩耍时间 int arrive_time, start_time, play_time; bool isvip; // 是否是 vip }; // 桌子 struct Table { // 当前服务结束时间,已服务玩家个数 // 刚开始全是8:00开始服务 int end_time = club_open_time, serve_count; bool isvip; // 是否是为vip预留的桌子 }; // 保存玩家,和 桌子 vector<Player> player; vector<Table> table; // 比较某个vip是否已经被服务过了 bool served[10000]; // 按到达的先后顺序排队 bool cmp1(Player a, Player b) { return a.arrive_time < b.arrive_time; } // 输出时,按被服务时间排序 bool cmp2(Player a, Player b) { return a.start_time < b.start_time; } // 将某个桌子提供给某个玩家 void AssignTableForPlayer(int t_id, int p_id) { // 玩家来的时候,这个桌子已空闲,玩家可以直接开始玩 if (table[t_id].end_time <= player[p_id].arrive_time) { player[p_id].start_time = player[p_id].arrive_time; } else { // 玩家来的时候这个桌子还在服务上一个人,需要等它当前服务结束 // 所以玩家开始玩的时间应该是这个桌子当前服务结束的时间 player[p_id].start_time = table[t_id].end_time; } // 开始新的服务,更新这个桌子当前服务的结束时间 table[t_id].end_time = player[p_id].start_time + player[p_id].play_time; // 这个桌子的服务人数增加 table[t_id].serve_count++; } // 找到这个人后面第一个会员未被服务的会员,被服务过了就跳过,如果不存在就返回-1 int FindNextVipPlayer(int p_id) { p_id++; while (p_id < player.size()) { // 是会员!且未被服务! if (player[p_id].isvip && !served[p_id]) return p_id; p_id++; } return -1; } int main() { // n个玩家,k个桌子,m个vip桌子 int n, k, m; cin >> n; Player p; int hh, mm, ss, t, flag; while (n-- > 0) { scanf("%d:%d:%d %d %d", &hh, &mm, &ss, &t, &flag); int arrive = hh * 3600 + mm * 60 + ss; // 玩家来的时候俱乐部关门,不用管他了 if (arrive >= club_close_time) continue; p.arrive_time = arrive; // 一个玩家最多玩2小时 t = t * 60; if (t > 7200) t = 7200; p.play_time = t; // 是否是vip p.isvip = flag == 1 ? true : false; // 把玩家被服务时间初始化为俱乐部关门时间,便于最后输出时淘汰掉哪些未被服务的玩家 p.start_time = club_close_time; // 保存当前玩家 player.push_back(p); } cin >> k >> m; // 桌子从1到K编号 table.resize(k + 1); // 读入m个vip桌子序号 int t_id; while (m-- > 0) { cin >> t_id; table[t_id].isvip = true; } // 玩家按到达时间排队 sort(player.begin(), player.end(), cmp1); int i = 0; // 逐个处理玩家 while (i < player.size()) { // 找到第一个结束服务的桌子 int index = -1, min_end = INT_MAX; for (int j = 1; j <= k; ++j) { if (table[j].end_time < min_end) { index = j; min_end = table[j].end_time; } } // 最早结束的桌子结束当前服务都要等到俱乐部关门了,顾客可以全回家了,没戏了 if (table[index].end_time >= club_close_time) break; // 这个桌子是给会员留的 if (table[index].isvip) { // 这个人也是会员 if (player[i].isvip) { // 并且没被服务过,就直接分配给他, if (!served[i]) { AssignTableForPlayer(index, i); // 标记这个会员被服务 served[i] = true; // 然后处理下一个人,所以 i++ i++; } else { // 服务过了就直接下一个 i++; } // 他是普通人 } else { // 找到他后面第一个vip // 如果不存在,或者 存在,但是当前桌子结束服务的时候这个vip还没来, // 他才可以用这个桌子, int vip = FindNextVipPlayer(i); if (vip == -1 || player[vip].arrive_time > table[index].end_time) { AssignTableForPlayer(index, i); // 然后处理下一个人,所以 i++ i++; } else { // 他后面有会员,而且这个会员的到达时间在这个桌子结束服务之前, // 这个桌子就给会员用 AssignTableForPlayer(index, vip); // 标记这个会员被服务 served[vip] = true; // 相当于这个人被插队了,下一个还是处理他,所以 i不变 } } // 普通桌子 } else { // 这个人是普通人就分配给他, if (!player[i].isvip) { AssignTableForPlayer(index, i); // 然后处理下一个人,所以 i++ i++; // 这个人是会员,并且没被服务过, } else if (!served[i]) { // 先看所有给会员预留的桌子是否有空闲,有就给他,没有就把这个普通桌子给他 // 找到所有会员桌中最早结束的那个 int t_vip = -1, t_vip_min_end = INT_MAX; for (int j = 1; j <= k; ++j) { if (table[j].isvip && table[j].end_time < t_vip_min_end) { t_vip = j; t_vip_min_end = table[j].end_time; } } // 最早结束的会员桌在他来之前服务完了,说明有可用的会员桌,分配给他 if (t_vip != -1 && table[t_vip].end_time <= player[i].arrive_time) { AssignTableForPlayer(t_vip, i); // 标记这个会员被服务 served[i] = true; // 然后处理下一个人,所以 i++ i++; } else { // 没有可用的会员桌,就把当前这个普通桌子给他 AssignTableForPlayer(index, i); // 标记这个会员被服务 served[i] = true; // 然后处理下一个人,所以 i++ i++; } // 这个人是会员但是被服务过了就直接处理下一个 } else { i++; } } } // 处理完所有玩家,按照被服务的开始时间排序 sort(player.begin(), player.end(), cmp2); // 输出结果,无法被服务的自动排除 for (auto it : player) { if (it.start_time >= club_close_time) continue; // 到达时间 printf("%02d:%02d:%02d ", it.arrive_time / 3600, it.arrive_time % 3600 / 60, it.arrive_time % 60); // 被服务时间 printf("%02d:%02d:%02d ", it.start_time / 3600, it.start_time % 3600 / 60, it.start_time % 60); // 等待时间 printf("%.0f\n", round((it.start_time - it.arrive_time) / 60.0)); } // 每个桌子服务了几个人 printf("%d", table[1].serve_count); for (int i = 2; i <= k; ++i) printf(" %d", table[i].serve_count); return 0; }说实在的,这个题,只要分类讨论细节想到位了,真的不难!想不到位就多想想呗,把我这个博客多看几遍你绝对明白!啊哈哈哈!