外卖店优先级问题(双指针降低时间复杂度)

外卖店优先级问题

问题详情

“饱了么”外卖系统中维护着 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≤105
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 号) 在优先缓存中。

问题分析

1、普通思路

时间复杂度O(n^2),会超时

1、1输入储存问题

由题意分析,我们最后取 t 时刻的优先队列状态,自然需要对之前输入的数据进行排序,按字典序正序排列进行存储

for(int i=1;i<=m;i++) {
        int ts,id;
        cin>>ts>>id;
        order[i]={ts,id};
    }
    sort(order+1,order+1+m);

这里直接放出代码,至于排序一般都是按照字典序来的,直接使用sort函数即可,如果各位觉得不保险的话可以使用结构体的自定义排序手法

struct Edge{
    int a,b;
    bool operator <(const Edge &W) const
    {
        if(a==W.a) return b<W.b;
        else return a<W.a;
    }
}edges[N];
1、2 遍历存储的数据

这里我们就需要 数组 f [ N ],来记录每个 id 的优先级,数组 s t [ N ] 来设置优先队列当中存储了哪些 id ;数组 used [ N ] 记录哪些时间点没被重复使用
每次拿出一个数据进行分析,对该时刻的 id 对应的优先级 +=2,其他 id -1操作,这里的时间复杂度是O(N * N)

1、3遍历 st [ N ] 数组,获得最终值

总结下来思路简单,代码实现也不难,但是会超时,所以有没有更好的方法呢?

2、双指针【时间复杂度 O( n )】

和上面的思路部分一致
需要 数组 f [ N ],来记录每个 id 的优先级
数组 s t [ N ] 来设置优先队列当中存储了哪些 id
还需要数组 last [ N ]记录该 id 上一次更新的时间【这一步简化了修改每一个时间点每一个ID对应的优先级过程】

代码【具体的解析都在注释当中,其中困惑的点都会距离说明】

#include<iostream>
#include<algorithm>
using namespace std;
const int N =1e5 + 7;
int f[N],last[N];
bool st[N];
int n,m,T;
typedef pair<int,int> PII;
PII order[N];
int main(){
    cin>>n>>m>>T;
    for(int i=1;i<=m;i++) {
        int ts,id;
        cin>>ts>>id;
        order[i]={ts,id};
    }
    sort(order+1,order+1+m);
    //输入的起始下标是 1 ,所以排序的时候要改成 1 开始
    for(int i=1;i<=m;){//这里就是双指针操作, i 的增长会在循环中实现
        int j=i;
        while(order[i]==order[j] && j<=m) j++;
        //为什么这里的 j 可以<=m 呢?
        // 注意下面的 j-i ,这里代表的是更新到某 id 时应该增加“+=2”的次数
        //同时这里还考虑到了多次在相同 id 上面下单的情况
        int t=order[i].first,id=order[i].second,cnt=j-i;
        i=j;//这里对i进行更新维护,注意不要和上面那一行代码顺序搞反了!!!!
        f[id]-=t-last[id]-1;//这里举个例子大家就很容易理解了
    	//假设上次更新的时间是 3s ,这次更新的时间是 5s,
    	//那么在 3~5s 的时间里,我们减少了 5- 3 -1 的优先级
    	//为什么要减 1 呢?
    	//因为要把 5s 排除在外,【减去的数值应当是没有处于更新状态下的时间段!!!】
        if(f[id]<0) f[id]=0;
        //开始更新维护 f [ N ],防止其储存负数
        if(f[id]<=3) st[id]=false;
        f[id]+=cnt*2;
        if(f[id]>5) st[id]=true;
        last[id]=t;//更新上次更新的时间为此次 t 
    }
    int res=0;
    //下面是对 T 时刻的遍历,遍历对象是每个 ID
    for(int i=1;i<=n;i++){
        if(last[i]<T){
            f[i]-=T-last[i];//这里我们的更新为什么没有 - 1 操作呢 ?
            //我们再举个例子就知道了
			//假设最终时间是 T =5 ,上次更新时间是 t = 3s;
			//并且我们在第 5 秒的时候没有进行更新,不用将 第 5 秒排除在外
			//所及计算结果是 f[ i ] -= 5 - 3 ;
            if(f[i]<=3) st[i]=false;
        }
    }
    for(int i=1;i<=n;i++) res+=st[i];//计算出最终结果
    cout<<res<<endl;
}
### 蓝桥杯 外卖优先级 Java 实现 解题思路 #### 1. 题目分析 题目描述了一种动态变化的优先级机制,其中每家外卖优先级随时间和订单数量的变化而调整。具体规则如下: - 如果某外卖在某一时间单位内无订单,则其优先级减少 1,但不会低于 0[^1]。 - 若该外卖在此时间内有订单,则优先级增加 2,对于每一单均如此处理[^2]。 因此,解题的核心在于如何高效地计算各外卖在指定时间 `t` 的最终优先级,同时避免因数据规模较大而导致的时间或空间复杂度过高问题[^3]。 --- #### 2. 数据结构设计 为了满足性能需求并简化逻辑,可以采用以下两种主要的数据结构: ##### (1) 哈希表 (`HashMap`) 哈希表用于存储每个时间节点上发生订单的外卖集合。键表示某个特定时间点 `t`,值是一个列表,保存在该时间点下单的所有外卖编号[^4]。 ```java Map<Integer, List<Integer>> orderMap = new HashMap<>(); ``` ##### (2) 数组 定义一个长度为 `N` 的整型数组,用来记录每一家外卖的当前优先级状态。初始化时,所有外卖优先级均为 0。 ```java int[] priorityArray = new int[N]; Arrays.fill(priorityArray, 0); ``` --- #### 3. 算法流程 以下是基于上述数据结构的具体实现步骤: ##### (1) 输入解析 读取输入数据,构建 `orderMap` 表示各个时间段内的订单情况。假设输入格式为多行数据,每行为三个整数:`time`, `store_id`, 和 `orders_count`,分别对应时间、外卖编号以及此时间下的订单量。 ```java for (int i = 0; i < M; i++) { String[] line = br.readLine().split(" "); int time = Integer.parseInt(line[0]); int storeId = Integer.parseInt(line[1]); if (!orderMap.containsKey(time)) { orderMap.put(time, new ArrayList<>()); } orderMap.get(time).add(storeId); } ``` ##### (2) 动态更新优先级 遍历从 `0` 到目标时间 `T` 的每一个时间点,在每次迭代中完成两部分操作: - **降低未接单铺的优先级**:检查哪些铺在过去一段时间里没有任何新订单,并对其执行 `-1` 操作; - **提升已接单铺的优先级**:依据当前时间点上的订单信息,对相应铺实施 `+2 * orders_count` 更新。 注意优化效率,仅针对实际存在活动的时间节点进行详细运算即可。 ```java Set<Integer> activeStores = new HashSet<>(); // 初始化活跃铺为空集 activeStores.clear(); for (int currentTime = 0; currentTime <= T; currentTime++) { // 对于非活跃铺降权 for (int s = 1; s <= N; s++) { if (!activeStores.contains(s)) { priorityArray[s - 1] = Math.max(0, priorityArray[s - 1] - 1); } else { activeStores.remove(s); // 清除标记以便下一轮重新评估 } } // 当前时刻若有新增订单则提权 if (orderMap.containsKey(currentTime)) { List<Integer> storesWithOrders = orderMap.get(currentTime); for (Integer store : storesWithOrders) { priorityArray[store - 1] += 2; activeStores.add(store); // 添加至活跃铺集合以防重复扣分 } } } ``` --- #### 4. 输出结果 最后打印出所有外卖在给定结束时间后的最终优先级数值。 ```java StringBuilder sb = new StringBuilder(); for (int p : priorityArray) { sb.append(p).append("\n"); } System.out.print(sb.toString()); ``` --- ### 总结 以上方法利用了高效的哈希映射和简单的线性扫描技术来解决大规模数据场景中的动态优先级管理问题。它不仅能够有效控制运行时间成本,还兼顾了内存使用的合理性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帆与翔的网工之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值