算法 — 外卖店优先级

题目描述

"饱了么"外卖系统中维护着 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 ≤ 10^{5},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^{2}),假设现在 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);
    }
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值