阅览室
Problem Description
一个阅览室每天都要接待大批读者。阅览室开门时间是O,关门时间是T(T≤1000)。每位读者的到达时间都不一样,并且想要阅读的刊物不超过5本。每位读者心里对自己想看的刊物都有一个排位,到达之后他会先去找自己最想看的刊物,如果找不到则去找其次想看的刊物。如果找不到任何他想看的刊物,他会开始等待,直到有一本以上的他想看的刊物被人放回原处。当然,他会先去拿其中自己最想看的刊物。当他看完某一本刊物后,就把它放回原处,接着去找自己没看过的最想看的刊物。如此下去,直到看完所有他想看的刊物为止。矛盾出现在两个人同时想要拿同一本刊物的时候。阅览室为了避免读者之间出现争执,作了一个规定,读者每次在开始等待时先去服务台做一次登记。如果两个人都同时想要一本刊物,那么先登记的读者将得到这本刊物。如果两个人同时登记,那么先到达阅览室的读者将得到刊物。没得到的人就只能去找其他的刊物看。阅览室关门时,所有读者都将被强迫离开阅览室,不再允许继续阅读。
现在阅览室想做一个统计调查,你被要求写一个程序来模拟这个过程计算出所有刊物被阅读的总次数。当某个读者开始阅读某本刊物时,该刊物的被阅读次数就加1,无论这本刊物最后有没有被读完。
Input
输入包括了多个测试数据。每个测试数据开头是两个整数T和n(1≤n≤100),分别表示图书馆关门时间和读者总数。接下来按照读者的到达时间先后依次给出了每位读者的具体描述。每个读者描述开头是一个整数t(0≤t<T),表示该读者到达时间。接下来一行开头是一个整数k(1≤k≤5),表示该读者想要看的刊物数目。之后跟着2k个整数按照读者想要阅读的刊物的顺序依次给出了刊物的描述。其中第2i-1个整数表示刊物的编号s(0≤s<1000),第2i个整数表示该读者读完这本刊物所需的时间。
Output
对于每个测试数据,在单独一行里输出所有刊物被阅读的总次数。
Sample Input
10 4
1
2 1 4 2 5
3
1 2 4
7
3 2 2 1 3 3 2
9
1 4 2
Sample Output
5
观察本题,我们很容易知道,这是一道简单的模拟题。题目要求我们模拟图书馆开始的过程中,读者的阅读过程。
那么,我们就先理清思路,打出这道模拟题。
因此,我们发现了这道题中,图书馆的如下特征。
- 本题通过题目描述,所有事件的发生的内容和时间都是确定的,不存在多种可能。
- 图书馆在时间0就开门,因此读者可以在0时来图书馆。
- 读者一旦到达图书馆,或者书本读完,他就处于等待状态。
- 读者读书是有顺序的,除非他更喜欢的书已经读过或正在被读,他不会选择另一本他喜欢的书。
- 先到达阅览室的读者会在前面被描述,因此,我们在给读者编号的时候,他们的编号可以作为他们各自到达图书馆的顺序。
- 一旦到达时间T,图书馆就会关门,因此时间T不允许读书。
- 一旦书被读,我们就可以将读书此书+1
那么,我们就认真分析本题。
首先,为了算法的效率更高,我们希望让它的判断更快,那么,如果时间t没有人读完了书,并且没有人来,我们就可以跳过这个时间。
因此,我们可以通过一些特殊的结构来记录该时间是否出现了什么事件。记录的事件包括 读者 读完了 某书 或者 读者 来到了图书馆。
其实,因为书本的编号 i < 1000 ,那么,我们可以将事件统一起来,将读者来到了图书馆 等效于 读者读完了编号为1000的书本。这样做,可以减少一些特判,让我们的算法更加简洁。
另外,我们再观察,因为对于一群等待的人,他们各自应读什么书,我们应该从最早开始等待(并在这群最早开始等待的人中最早来到图书馆)开始,逐个逐个给他们他们当前可读的最想读的书。
因此,我们可以在每次有人读完书后,优先考虑其开始等待时间,其次考虑其编号大小, 对所有等待的人进行一个排序,而我们通过这个排序,依次给予读者他们想读的书。这样的简单模拟到最后,就可以直接输出答案了。
当然,提醒一下,对于这样比较复杂的模拟题,通过注释能够让我们更容易知道我们各个变量的内涵。
#include <cstdio>
#include <cstring>
using namespace std;
int T,ti,n,star; //T:闭馆时间 ti现在时间 n人数
int ans;//多少本书被读了多少遍
bool q[1020]; //书是否被读
int p[110]; //开始等待的时间
struct node{
int va,ra;
}nowq[110];//临时队列,记录闲置的人
int tail;// 队列长度
struct book{ //喜欢的书
int nu[6],t[6];
bool have[6];
int k;
}b[110];//b:人 nu:书 t:时间 k: 书的数量 have:读过没
int h[1010];//什么时间的事件戳
struct read{
int from,to,next;
}r[5000010]; //r:事件编号 from:谁 to:书 next:下一个
int tot; //事件总数
void sort(int ,int );
int main()
{
while (scanf("%d%d",&T,&n)!=EOF)
{
memset(q,0,sizeof(q));
memset(h,0,sizeof(h));
ans=tot=ti=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&star);
r[++tot].from=i;r[tot].to=1000;r[tot].next=h[star];h[star]=tot;p[i]=-1;
scanf("%d",&b[i].k);
for (int j=1;j<=b[i].k;j++)
{
scanf("%d%d",&b[i].nu[j],&b[i].t[j]);
b[i].have[j]=false;
}
}
while (ti<T)
{
if (h[ti])
{
for (int jump=h[ti];jump;jump=r[jump].next)
{
q[r[jump].to]=false;
p[r[jump].from]=ti;
}
tail=0;
for (int i=1;i<=n;i++)
if (p[i]!=-1)
nowq[++tail].ra=i,nowq[tail].va=p[i];
sort(1,tail);
for (int x=1;x<=tail;x++)
{
int i=nowq[x].ra;
for (int j=1;j<=b[i].k;j++)
{
if ((!q[b[i].nu[j]])&&(!b[i].have[j]))
{
q[b[i].nu[j]]=true;
b[i].have[j]=true;
if (ti+b[i].t[j]<=T)
{
r[++tot].from=i;
r[tot].to=b[i].nu[j];
r[tot].next=h[ti+b[i].t[j]];
h[ti+b[i].t[j]]=tot;
}
p[i]=-1;
ans++;
goto out;
}
}
out :;
}
}
ti++;
}
printf("%d\n",ans);
}
return 0;
}
void sort(int l,int r)
{
int a=l,b=r;
int mid1=nowq[(a+b)>>1].va,mid2=nowq[(a+b)>>1].ra;
while (a<=b)
{
while ((nowq[a].va<mid1)||((nowq[a].va==mid1)&&(nowq[a].ra<mid2)))
a++;
while ((nowq[b].va>mid1)||((nowq[b].va==mid1)&&(nowq[b].ra>mid2)))
b--;
if (a<=b)
{
node f;
f=nowq[a];
nowq[a]=nowq[b];
nowq[b]=f;
a++;b--;
}
}
if (a<r)
sort(a,r);
if (l<b)
sort(l,b);
return ;
}