题目描述
"饱了么"外卖系统中维护着 N 家外卖店,编号 1 ∼ N。每家外卖店都有 一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减 到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果 优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中?
输入描述
第一行包含 3 个整数 N , M , T 。
以下 M 行每行包含两个整数 ts , id ,表示 ts 时刻编号 id 的外卖店收到一个订单。
其中,1 ≤ N , M , T ≤ ,1 ≤ ts ≤ T,1 ≤ id ≤ N。
输出描述
输出一个整数代表答案。
输入输出样例
示例
输入:
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
输出:
1
样例解释:
6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6, 加入优先缓存。所以是有 1 家店 (2 号) 在优先缓存中。
运行限制
- 最大运行时间:2s
- 最大运行内存: 256M
解题思路:
这是蓝桥杯中的一道 20 分的编程题,注意解题过程中不能像之前解答填空题那样不顾算法效率。首先我们一开始想到的是暴力解法,即对于预排序好的订单,我们查找该订单的外卖店 id,然后将 id 对应的外卖店优先级进行 + 2 操作,之后遍历所有外卖店,将所有 id 不等于该订单外卖店 id 的外卖店的优先级都进行 - 1 操作(当然都会先进行判断是否该外卖店的优先级 > 0),这样下来算法效率为 O(),假设现在 N、M、T 都达到最大值的话,则需要进行的基本操作次数是 10,000,000,000 次,故不是一个明智的选择,但是代码我也同样写在下放,Java 实现如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int N = scan.nextInt();
int M = scan.nextInt();
int T = scan.nextInt();
int[][] arr = new int[M][2];
int[][] takeAway = new int[N + 1][2];
int i, j, minNode, temp, sum = 0;
for (i = 0; i < M; i++) {//系统输入
for (j = 0; j < 2; j++) {
arr[i][j] = scan.nextInt();
}
}
for (i = 1; i <= N; i++) {//初始化外卖店编号
takeAway[i][0] = i;
}
for (i = 0; i < M - 1; i++) {
minNode = i;
for (j = i + 1; j < M; j++) {
if (arr[i][0] > arr[j][0]) {
minNode = j;
}
}
temp = arr[minNode][0];
arr[minNode][0] = arr[i][0];
arr[i][0] = temp;
temp = arr[minNode][1];
arr[minNode][1] = arr[i][1];
arr[i][1] = temp;
}
for (i = 0; i < M; i++) {
takeAway[arr[i][1]][1] += 2;
for (j = 1; j <= N; j++) {
if (arr[i][1] != j) {
if (takeAway[j][1] > 0) {
takeAway[j][1] -= 1;
}
}
}
}
for (i = 1; i <= N; i++) {
if (takeAway[i][1] > 5) {
sum++;
}
}
System.out.println(sum);
scan.close();
}
}
以上代码是不能通过蓝桥杯测评系统的检测的,那么我们就要寻找其他的巧解。首先我们可以知道,第一种思路中,双层 for 循环为了解决每一次的优先级加减问题而导致算法效率大大降低,其实这里可以通过一个 Map 来达到一个更好的效率。Map 的每一个 key 对应一个外卖店的 id,每一个 value 对应的是一个 ArrayList 集合,里面放的是时刻,也就是该外卖店在哪一个时刻收到了订单,故这样只需要通过对该 Map 的每一个外卖店 id 进行遍历,求出每一个 id 对应的时刻外卖单数量(循着这个思路看代码就不会那么吃力),然后根据题目的要求,某时刻有订单则优先级 + 2,没有订单且目前优先级 > 0 的情况下就 - 1,否则就不变(也就是优先级最低为 0)。该思路算法的 Java 代码实现如下:
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.TreeMap;
public class Main {
static int N, M, T;
static Map<Integer, ArrayList<Integer>> map = new TreeMap<>();
static int sum;// 用于记录在优先缓存中的外卖店数量
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
N = scan.nextInt();// 外卖店数量
M = scan.nextInt();// 订单数量
T = scan.nextInt();// 时刻
for (int i = 1; i <= M; i++) {//系统输入初始化
int time = scan.nextInt();
int id = scan.nextInt();
if (map.containsKey(id)) {
map.get(id).add(time);
} else {
ArrayList<Integer> temp = new ArrayList<>();
temp.add(time);
map.put(id, temp);
}
}
ArrayList<Map.Entry<Integer, ArrayList<Integer>>> list = new ArrayList<>(map.entrySet());
for (int i = 0; i < list.size(); i++) {
Entry<Integer, ArrayList<Integer>> entry = list.get(i);
ArrayList<Integer> lis = entry.getValue();
int num = 0;//表示该外卖店优先级数
int[] count = new int[T + 1];//用于记录该外卖店每一时刻的外卖单数量
boolean flag = false;//标记最终该外卖店是否处于优先缓存中
for (int j = 0; j < lis.size(); j++) {
count[lis.get(j)]++;//表示该外卖店在某个时刻的订单数
}
for (int j = 1; j <= T; j++) {//对每一个时刻的订单数进行遍历并判断
if (count[j] == 0) {
if (num > 0) {
num--;
}
if (num <= 3) {
flag = false;
}
} else {
num += count[j] * 2;
if (num > 5) {
flag = true;
}
}
}
if (flag) {
sum++;
}
}
System.out.println(sum);
}
}