传送门:AcWing 1241. 外卖店优先级 / 蓝桥杯2019年第十届真题
思路
将相同时刻的订单汇总到一起,然后遍历每个时刻。
只考虑有订单的时刻,对于时刻 i i i,先汇总相同店的订单数,然后遍历所有订单,每个订单包含店的编号信息(假设店编号为 i d id id),维护店 i d id id上次出现订单的时刻(假设为 l a s t [ i d ] last[id] last[id]),那么店 i d id id之前有 i − l a s t [ i d ] − 1 i-last[id]-1 i−last[id]−1 的时间都没有订单,此时优先级需要先减去这段没有订单的时间,若优先级<=3,则将店 i d id id移出缓存队列;然后优先级加上 n u m num num个订单带来的优先级 2 ∗ n u m 2*num 2∗num,若优先级>5,则进入缓存队列。
最后还需要遍历所有 T T T时刻无订单的店,它们最后一次出现订单的时刻到 T T T时刻这段时间就是其优先级的减小值,同时更新是否被移出缓存队列。
T T T个时刻, m m m个订单, n n n个店,时间复杂度大约是 O ( T + m + n ) O(T+m+n) O(T+m+n),因为对于小于 T T T的时刻并没有更新所有店的优先级,而只是更新了有订单的店的优先级,只需要在最后的 T T T时刻更新一次所有店的优先级即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,T,last[N],sum[N];
bool f[N]; // 是否在缓存队列中
vector<int>ans[N];
unordered_map<int,int>vis;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>T;
int t,id;
for(int i=1;i<=m;i++)
{
cin>>t>>id;
ans[t].push_back(id); // t时刻编号为id的店有订单
}
memset(last,-1,sizeof(last));
for(int i=1;i<=T;i++) // 遍历时间
{
if(ans[i].size()) // i时刻有订单
{
int sz=ans[i].size();
vis.clear();
for(int j=0;j<sz;j++)
{
int id=ans[i][j];
vis[id]++; // 汇总相同店的订单数
}
for(auto it:vis)
{
int id=it.first; // 店id
int num=it.second; // 订单数num
if(last[id]==-1) // 店id第1次出现
{
sum[id]=num*2;
last[id]=i;
if(sum[id]>5)f[id]=1; // 放入缓存队列
}
else
{
int d=i-last[id]-1;
sum[id]=max(0,sum[id]-d);
if(sum[id]<=3)f[id]=0; // 移出缓存队列
sum[id]+=num*2;
if(sum[id]>5)f[id]=1; // 放入缓存队列
last[id]=i;
}
}
}
if(i==T) // 处理T时刻无订单的店
{
for(int j=1;j<=n;j++)
{
if(last[j]!=T) // 店j的最后一次订单出现在T时刻之前
{
int d=T-last[j];
sum[j]=max(0,sum[j]-d);
if(sum[j]<=3)f[j]=0; // 移出缓存队列
}
}
}
}
int cnt=0;
for(int i=1;i<=n;i++)
if(f[i])cnt++;
printf("%d\n",cnt);
return 0;
}
/*
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
ans:1
2 6 8
1 1
5 2
3 1
6 2
2 1
6 2
ans:1
2 6 9
1 1
5 2
3 1
6 2
2 1
6 2
ans:0
1 3 5
1 1
2 1
3 1
ans:1
1 3 6
1 1
2 1
3 1
ans:0
*/