题目描述
"饱了么"外卖系统中维护着 N 家外卖店,编号 1 ~ N。每家外卖店都有一个优先级,初始时(0 时刻)优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中。
输入格式
第一行包含 3 个整数 N 、 M 和 T。
以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到一个订单。
输出格式
输出一个整数代表答案。
输入
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
输出
1
说明/提示
样例解释
6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6,加入优先缓存。所以有 1 家店(2 号店)在优先缓存中。
评测用例规模与约定
对于 80% 的评测用例,1≤N,M,T≤10000。
对于所有评测用例,1≤N,M,T≤10^5,1≤ts≤T,1≤id≤N。
cs
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 100010
typedef struct {
int time;
int id;
} Order;
// 比较函数:先按店铺id排序,id相同的按时间排序
int cmp(const void *a, const void *b) {
Order *x = (Order *)a;
Order *y = (Order *)b;
if (x->id != y->id) {
return x->id - y->id;
}
return x->time - y->time;
}
int main() {
int N, M, T;
scanf("%d %d %d", &N, &M, &T);
Order orders[M];
for (int i = 0; i < M; i++) {
scanf("%d %d", &orders[i].time, &orders[i].id);
}
// 按店铺和时间排序订单
qsort(orders, M, sizeof(Order), cmp);
// 初始化每个店铺的状态
int priority[MAX_N] = {0}; // 用于计算优先级
int last_time[MAX_N] = {0}; // 上一次有订单的时间,用于和当前订单时间作差,找到要减去的数值
int in_cache[MAX_N] = {0}; // 是否在优先缓存中。如果是1就说明在优先缓存中,初始化为0,因为一开始肯定没在优先缓存中
int result = 0;//用于输出最后结果
int idx = 0; // 当前处理的订单索引
//订单索引就是订单的下标。共有M条订单,所以条件就是idx<M。
// 遍历每个店铺。一个店铺一个店铺的遍历,完全处理完一个店铺之后,即找到该店铺是否在缓存中后,再遍历其他店铺。
for (int id = 1; id <= N; id++) {
// 处理该店铺,即店铺1的所有订单
//while循环的条件就是判断是否是店铺1,是店铺1才进行下面操作。
while (idx < M && orders[idx].id == id) {
int ts = orders[idx].time;//当前的时刻赋值给ts。
// 计算从上一次订单到这次订单之间减少的优先级
if (last_time[id] != ts) {
priority[id] -= (ts - last_time[id] - 1);
if (priority[id] < 0) priority[id] = 0;
}
// 检查是否要移出缓存
if (in_cache[id] && priority[id] <= 3) {
in_cache[id] = 0;//表示不在优先缓存里边
}
// 处理当前订单
priority[id] += 2;
last_time[id] = ts;//当前时间处理完成,下一次进行的时候就变成上一时刻了。
// 检查是否要加入缓存
if (priority[id] > 5) {
in_cache[id] = 1;
}
idx++;//不断加1,直到处理完该店铺的所有订单
}
// 处理从最后一次订单到T时刻的优先级减少
//避免出现最后一次时刻不在样例输入中,但店铺还是要减优先级的情况。最后再进行一次缓存判断,输出结果。
if (last_time[id] < T) {
priority[id] -= (T - last_time[id]);
if (priority[id] < 0) priority[id] = 0;
if (in_cache[id] && priority[id] <= 3) {
in_cache[id] = 0;
}
}
// 统计结果
if (in_cache[id]) {
result++;
}
}
printf("%d\n", result);
return 0;
}
cs
int cmp(const void *a, const void *b) {
Order *x = (Order *)a;
Order *y = (Order *)b;
if (x->id != y->id) {
return x->id - y->id;//先按店铺id升序
}
return x->time - y->time;//id相同按时间排序
}
上述代码是快速排序,下面详细解析一下:
Order *x = (Order *)a; // 将void指针转换为Order指针
-
转换 :
(Order *)a告诉编译器:"把a当作指向Order的指针" -
使用 :现在
x是Order *,可以用x->id访问成员
注意上述排序顺序。一开始我以为只需要进行时刻排序,但发现反正还要进行优先级减法,所以都一样。
怎样保证是升序还是降序呢?
|-------------|---------|---------|----------------|
| 升序(小在前) | 返回 <0 | 返回 >0 | return a - b |
| 降序(大在前) | 返回 >0 | 返回 <0 | return b - a |
对于升序比较函数:return a-b
| 情况 | 返回值 | 含义(升序视角) | 排序结果 |
|---|---|---|---|
a < b |
负数 | "a 比 b 小,应该在前" |
a 在 b 前 |
a = b |
零 | "a 和 b 相等" |
顺序不确定 |
a > b |
正数 | "a 比 b 大,应该在后" |
b 在 a 前 |
对于降序比较函数:return b-a
| 情况 | 返回值 | 含义(降序视角) | 排序结果 |
|---|---|---|---|
a > b |
负数 | "a 比 b 大,应该在前" |
a 在 b 前 |
a = b |
零 | "a 和 b 相等" |
顺序不确定 |
a < b |
正数 | "a 比 b 小,应该在后" |
b 在 a 前 |
例子:数字 3 和 5
情况1:升序比较函数
cs
int cmp_asc(const void *a, const void *b) {
int x = *(int *)a; // 假设 a=3, b=5
int y = *(int *)b;
return x - y; // 3-5 = -2 < 0
}
-
返回负数(-2)
-
指令:"把 3 放在 5 前面"
-
结果:
3, 5(升序)
情况2:降序比较函数
cs
int cmp_desc(const void *a, const void *b) {
int x = *(int *)a; // 假设 a=3, b=5
int y = *(int *)b;
return y - x; // 5-3 = 2 > 0
}
-
返回正数(2)
-
指令:"把 3 放在 5 后面"
-
结果:
5, 3(降序)